@sync-in/server 1.3.9 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -0
- package/README.md +5 -3
- package/environment/environment.dist.yaml +2 -0
- package/package.json +6 -6
- package/server/app.bootstrap.js +9 -0
- package/server/app.bootstrap.js.map +1 -1
- package/server/app.service.spec.js +44 -19
- package/server/app.service.spec.js.map +1 -1
- package/server/applications/comments/comments.controller.spec.js +103 -4
- package/server/applications/comments/comments.controller.spec.js.map +1 -1
- package/server/applications/comments/services/comments-manager.service.spec.js +409 -9
- package/server/applications/comments/services/comments-manager.service.spec.js.map +1 -1
- package/server/applications/files/adapters/files-indexer-mysql.service.spec.js +333 -0
- package/server/applications/files/adapters/files-indexer-mysql.service.spec.js.map +1 -0
- package/server/applications/files/constants/files.js +0 -23
- package/server/applications/files/constants/files.js.map +1 -1
- package/server/applications/files/constants/only-office.js +8 -0
- package/server/applications/files/constants/only-office.js.map +1 -1
- package/server/applications/files/constants/routes.js +6 -1
- package/server/applications/files/constants/routes.js.map +1 -1
- package/server/applications/files/files-only-office.controller.js +11 -0
- package/server/applications/files/files-only-office.controller.js.map +1 -1
- package/server/applications/files/files-only-office.controller.spec.js +97 -3
- package/server/applications/files/files-only-office.controller.spec.js.map +1 -1
- package/server/applications/files/files-tasks.controller.spec.js +91 -1
- package/server/applications/files/files-tasks.controller.spec.js.map +1 -1
- package/server/applications/files/files.config.js +5 -0
- package/server/applications/files/files.config.js.map +1 -1
- package/server/applications/files/files.controller.spec.js +268 -46
- package/server/applications/files/files.controller.spec.js.map +1 -1
- package/server/applications/files/guards/files-only-office.guard.spec.js +77 -1
- package/server/applications/files/guards/files-only-office.guard.spec.js.map +1 -1
- package/server/applications/files/guards/files-only-office.strategy.js +0 -1
- package/server/applications/files/guards/files-only-office.strategy.js.map +1 -1
- package/server/applications/files/services/files-only-office-manager.service.js +5 -0
- package/server/applications/files/services/files-only-office-manager.service.js.map +1 -1
- package/server/applications/links/links.controller.spec.js +91 -58
- package/server/applications/links/links.controller.spec.js.map +1 -1
- package/server/applications/links/services/links-manager.service.js +4 -6
- package/server/applications/links/services/links-manager.service.js.map +1 -1
- package/server/applications/links/services/links-manager.service.spec.js +378 -14
- package/server/applications/links/services/links-manager.service.spec.js.map +1 -1
- package/server/applications/links/services/links-queries.service.js +1 -1
- package/server/applications/links/services/links-queries.service.js.map +1 -1
- package/server/applications/notifications/notifications.controller.spec.js +56 -1
- package/server/applications/notifications/notifications.controller.spec.js.map +1 -1
- package/server/applications/notifications/services/notifications-manager.service.spec.js +461 -5
- package/server/applications/notifications/services/notifications-manager.service.spec.js.map +1 -1
- package/server/applications/shares/services/shares-manager.service.spec.js +590 -14
- package/server/applications/shares/services/shares-manager.service.spec.js.map +1 -1
- package/server/applications/spaces/guards/space.guard.spec.js +153 -18
- package/server/applications/spaces/guards/space.guard.spec.js.map +1 -1
- package/server/applications/spaces/services/spaces-browser.service.js +7 -7
- package/server/applications/spaces/services/spaces-browser.service.js.map +1 -1
- package/server/applications/spaces/services/spaces-manager.service.js +17 -17
- package/server/applications/spaces/services/spaces-manager.service.js.map +1 -1
- package/server/applications/sync/interceptors/sync-diff-gzip-body.interceptor.spec.js +120 -0
- package/server/applications/sync/interceptors/sync-diff-gzip-body.interceptor.spec.js.map +1 -0
- package/server/applications/sync/services/sync-clients-manager.service.spec.js +548 -8
- package/server/applications/sync/services/sync-clients-manager.service.spec.js.map +1 -1
- package/server/applications/sync/services/sync-manager.service.spec.js +837 -5
- package/server/applications/sync/services/sync-manager.service.spec.js.map +1 -1
- package/server/applications/sync/services/sync-paths-manager.service.spec.js +900 -7
- package/server/applications/sync/services/sync-paths-manager.service.spec.js.map +1 -1
- package/server/applications/sync/utils/routes.js +1 -1
- package/server/applications/sync/utils/routes.js.map +1 -1
- package/server/applications/users/guards/permissions.guard.js +4 -4
- package/server/applications/users/guards/permissions.guard.js.map +1 -1
- package/server/applications/users/guards/permissions.guard.spec.js +6 -6
- package/server/applications/users/guards/permissions.guard.spec.js.map +1 -1
- package/server/applications/users/guards/roles.guard.js +1 -1
- package/server/applications/users/guards/roles.guard.js.map +1 -1
- package/server/applications/users/models/user.model.js +1 -1
- package/server/applications/users/models/user.model.js.map +1 -1
- package/server/applications/users/services/admin-users-manager.service.js +22 -24
- package/server/applications/users/services/admin-users-manager.service.js.map +1 -1
- package/server/applications/users/services/admin-users-manager.service.spec.js +763 -17
- package/server/applications/users/services/admin-users-manager.service.spec.js.map +1 -1
- package/server/applications/users/services/users-manager.service.js +1 -1
- package/server/applications/users/services/users-manager.service.js.map +1 -1
- package/server/applications/users/services/users-manager.service.spec.js +938 -49
- package/server/applications/users/services/users-manager.service.spec.js.map +1 -1
- package/server/applications/webdav/decorators/if-header.decorator.js +4 -1
- package/server/applications/webdav/decorators/if-header.decorator.js.map +1 -1
- package/server/applications/webdav/filters/webdav.filter.spec.js +77 -0
- package/server/applications/webdav/filters/webdav.filter.spec.js.map +1 -0
- package/server/applications/webdav/guards/webdav-protocol.guard.js +3 -7
- package/server/applications/webdav/guards/webdav-protocol.guard.js.map +1 -1
- package/server/applications/webdav/guards/webdav-protocol.guard.spec.js +580 -0
- package/server/applications/webdav/guards/webdav-protocol.guard.spec.js.map +1 -0
- package/server/applications/webdav/services/webdav-methods.service.spec.js +1582 -3
- package/server/applications/webdav/services/webdav-methods.service.spec.js.map +1 -1
- package/server/applications/webdav/services/webdav-spaces.service.spec.js +390 -2
- package/server/applications/webdav/services/webdav-spaces.service.spec.js.map +1 -1
- package/server/applications/webdav/webdav.controller.js +2 -2
- package/server/applications/webdav/webdav.controller.js.map +1 -1
- package/server/authentication/guards/auth-basic.guard.js.map +1 -1
- package/server/authentication/guards/auth-basic.guard.spec.js +38 -2
- package/server/authentication/guards/auth-basic.guard.spec.js.map +1 -1
- package/server/authentication/guards/auth-basic.strategy.js +0 -1
- package/server/authentication/guards/auth-basic.strategy.js.map +1 -1
- package/server/authentication/guards/auth-digest.guard.js +1 -2
- package/server/authentication/guards/auth-digest.guard.js.map +1 -1
- package/server/authentication/guards/auth-local.guard.js.map +1 -1
- package/server/authentication/guards/auth-local.guard.spec.js +7 -5
- package/server/authentication/guards/auth-local.guard.spec.js.map +1 -1
- package/server/authentication/guards/auth-local.strategy.js +0 -1
- package/server/authentication/guards/auth-local.strategy.js.map +1 -1
- package/server/authentication/guards/auth-token-access.guard.spec.js +30 -0
- package/server/authentication/guards/auth-token-access.guard.spec.js.map +1 -1
- package/server/authentication/guards/auth-token-access.strategy.js +0 -1
- package/server/authentication/guards/auth-token-access.strategy.js.map +1 -1
- package/server/authentication/guards/auth-token-refresh.strategy.js +0 -1
- package/server/authentication/guards/auth-token-refresh.strategy.js.map +1 -1
- package/server/authentication/services/auth-methods/auth-method-database.service.js +1 -1
- 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 +8 -6
- 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 +2 -2
- 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 +500 -5
- package/server/authentication/services/auth-methods/auth-method-ldap.service.spec.js.map +1 -1
- package/server/configuration/config.loader.js +0 -3
- package/server/configuration/config.loader.js.map +1 -1
- package/server/infrastructure/context/interceptors/context.interceptor.spec.js +135 -0
- package/server/infrastructure/context/interceptors/context.interceptor.spec.js.map +1 -0
- package/server/infrastructure/context/services/context-manager.service.spec.js +98 -0
- package/server/infrastructure/context/services/context-manager.service.spec.js.map +1 -0
- package/server/infrastructure/database/constants.js +0 -1
- package/server/infrastructure/database/constants.js.map +1 -1
- package/server/infrastructure/database/scripts/seed/usersgroups.js +3 -3
- package/server/infrastructure/database/scripts/seed/usersgroups.js.map +1 -1
- package/server/infrastructure/mailer/mailer.service.js +20 -19
- package/server/infrastructure/mailer/mailer.service.js.map +1 -1
- package/server/infrastructure/mailer/mailer.service.spec.js +176 -0
- package/server/infrastructure/mailer/mailer.service.spec.js.map +1 -0
- package/static/3rdpartylicenses.txt +26 -26
- package/static/assets/pdfjs/build/pdf.mjs +1177 -255
- package/static/assets/pdfjs/build/pdf.mjs.map +1 -1
- package/static/assets/pdfjs/build/pdf.sandbox.mjs +25 -2
- package/static/assets/pdfjs/build/pdf.sandbox.mjs.map +1 -1
- package/static/assets/pdfjs/build/pdf.worker.mjs +140 -16
- package/static/assets/pdfjs/build/pdf.worker.mjs.map +1 -1
- package/static/assets/pdfjs/version +1 -1
- package/static/assets/pdfjs/web/debugger.css +31 -0
- package/static/assets/pdfjs/web/debugger.mjs +144 -2
- package/static/assets/pdfjs/web/images/comment-editButton.svg +6 -1
- package/static/assets/pdfjs/web/locale/ach/viewer.ftl +0 -63
- package/static/assets/pdfjs/web/locale/af/viewer.ftl +0 -71
- package/static/assets/pdfjs/web/locale/an/viewer.ftl +0 -63
- package/static/assets/pdfjs/web/locale/ast/viewer.ftl +0 -60
- package/static/assets/pdfjs/web/locale/az/viewer.ftl +0 -63
- package/static/assets/pdfjs/web/locale/be/viewer.ftl +38 -0
- package/static/assets/pdfjs/web/locale/bg/viewer.ftl +0 -37
- package/static/assets/pdfjs/web/locale/bn/viewer.ftl +0 -63
- package/static/assets/pdfjs/web/locale/bo/viewer.ftl +0 -63
- package/static/assets/pdfjs/web/locale/br/viewer.ftl +0 -37
- package/static/assets/pdfjs/web/locale/brx/viewer.ftl +0 -63
- package/static/assets/pdfjs/web/locale/bs/viewer.ftl +22 -0
- package/static/assets/pdfjs/web/locale/ca/viewer.ftl +0 -54
- package/static/assets/pdfjs/web/locale/cak/viewer.ftl +0 -54
- package/static/assets/pdfjs/web/locale/ckb/viewer.ftl +0 -63
- package/static/assets/pdfjs/web/locale/cs/viewer.ftl +38 -0
- package/static/assets/pdfjs/web/locale/cy/viewer.ftl +38 -0
- package/static/assets/pdfjs/web/locale/da/viewer.ftl +38 -0
- package/static/assets/pdfjs/web/locale/de/viewer.ftl +38 -0
- package/static/assets/pdfjs/web/locale/dsb/viewer.ftl +38 -0
- package/static/assets/pdfjs/web/locale/el/viewer.ftl +38 -0
- package/static/assets/pdfjs/web/locale/en-CA/viewer.ftl +38 -0
- package/static/assets/pdfjs/web/locale/en-GB/viewer.ftl +38 -0
- package/static/assets/pdfjs/web/locale/en-US/viewer.ftl +25 -0
- package/static/assets/pdfjs/web/locale/eo/viewer.ftl +38 -0
- package/static/assets/pdfjs/web/locale/es-AR/viewer.ftl +38 -0
- package/static/assets/pdfjs/web/locale/es-CL/viewer.ftl +38 -0
- package/static/assets/pdfjs/web/locale/es-MX/viewer.ftl +0 -6
- package/static/assets/pdfjs/web/locale/et/viewer.ftl +0 -57
- package/static/assets/pdfjs/web/locale/fa/viewer.ftl +0 -37
- package/static/assets/pdfjs/web/locale/ff/viewer.ftl +0 -63
- package/static/assets/pdfjs/web/locale/fi/viewer.ftl +38 -0
- package/static/assets/pdfjs/web/locale/fr/viewer.ftl +38 -0
- package/static/assets/pdfjs/web/locale/fy-NL/viewer.ftl +38 -0
- package/static/assets/pdfjs/web/locale/ga-IE/viewer.ftl +0 -71
- package/static/assets/pdfjs/web/locale/gd/viewer.ftl +0 -54
- package/static/assets/pdfjs/web/locale/gl/viewer.ftl +8 -0
- package/static/assets/pdfjs/web/locale/gn/viewer.ftl +38 -0
- package/static/assets/pdfjs/web/locale/gu-IN/viewer.ftl +0 -63
- package/static/assets/pdfjs/web/locale/he/viewer.ftl +38 -0
- package/static/assets/pdfjs/web/locale/hi-IN/viewer.ftl +0 -60
- package/static/assets/pdfjs/web/locale/hsb/viewer.ftl +38 -0
- package/static/assets/pdfjs/web/locale/hu/viewer.ftl +38 -0
- package/static/assets/pdfjs/web/locale/hy-AM/viewer.ftl +0 -49
- package/static/assets/pdfjs/web/locale/hye/viewer.ftl +0 -60
- package/static/assets/pdfjs/web/locale/ia/viewer.ftl +38 -0
- package/static/assets/pdfjs/web/locale/is/viewer.ftl +0 -3
- package/static/assets/pdfjs/web/locale/it/viewer.ftl +31 -0
- package/static/assets/pdfjs/web/locale/ja/viewer.ftl +8 -0
- package/static/assets/pdfjs/web/locale/ka/viewer.ftl +48 -10
- package/static/assets/pdfjs/web/locale/kab/viewer.ftl +5 -0
- package/static/assets/pdfjs/web/locale/kk/viewer.ftl +8 -0
- package/static/assets/pdfjs/web/locale/km/viewer.ftl +0 -63
- package/static/assets/pdfjs/web/locale/kn/viewer.ftl +0 -71
- package/static/assets/pdfjs/web/locale/ko/viewer.ftl +38 -0
- package/static/assets/pdfjs/web/locale/lij/viewer.ftl +0 -63
- package/static/assets/pdfjs/web/locale/lo/viewer.ftl +0 -54
- package/static/assets/pdfjs/web/locale/lt/viewer.ftl +0 -60
- package/static/assets/pdfjs/web/locale/ltg/viewer.ftl +0 -63
- package/static/assets/pdfjs/web/locale/lv/viewer.ftl +0 -63
- package/static/assets/pdfjs/web/locale/meh/viewer.ftl +0 -75
- package/static/assets/pdfjs/web/locale/mk/viewer.ftl +0 -63
- package/static/assets/pdfjs/web/locale/ml/viewer.ftl +0 -3
- package/static/assets/pdfjs/web/locale/mr/viewer.ftl +0 -63
- package/static/assets/pdfjs/web/locale/ms/viewer.ftl +0 -63
- package/static/assets/pdfjs/web/locale/my/viewer.ftl +0 -71
- package/static/assets/pdfjs/web/locale/nb-NO/viewer.ftl +44 -6
- package/static/assets/pdfjs/web/locale/ne-NP/viewer.ftl +0 -71
- package/static/assets/pdfjs/web/locale/nl/viewer.ftl +38 -0
- package/static/assets/pdfjs/web/locale/nn-NO/viewer.ftl +45 -1
- package/static/assets/pdfjs/web/locale/oc/viewer.ftl +0 -31
- package/static/assets/pdfjs/web/locale/pa-IN/viewer.ftl +38 -0
- package/static/assets/pdfjs/web/locale/pl/viewer.ftl +39 -1
- package/static/assets/pdfjs/web/locale/pt-BR/viewer.ftl +38 -0
- package/static/assets/pdfjs/web/locale/ro/viewer.ftl +355 -1
- package/static/assets/pdfjs/web/locale/ru/viewer.ftl +38 -0
- package/static/assets/pdfjs/web/locale/sat/viewer.ftl +0 -54
- package/static/assets/pdfjs/web/locale/sc/viewer.ftl +0 -38
- package/static/assets/pdfjs/web/locale/scn/viewer.ftl +0 -92
- package/static/assets/pdfjs/web/locale/sco/viewer.ftl +0 -60
- package/static/assets/pdfjs/web/locale/si/viewer.ftl +0 -51
- package/static/assets/pdfjs/web/locale/sk/viewer.ftl +38 -0
- package/static/assets/pdfjs/web/locale/skr/viewer.ftl +0 -27
- package/static/assets/pdfjs/web/locale/sl/viewer.ftl +8 -0
- package/static/assets/pdfjs/web/locale/son/viewer.ftl +0 -71
- package/static/assets/pdfjs/web/locale/sr/viewer.ftl +0 -33
- package/static/assets/pdfjs/web/locale/sv-SE/viewer.ftl +38 -0
- package/static/assets/pdfjs/web/locale/szl/viewer.ftl +0 -63
- package/static/assets/pdfjs/web/locale/ta/viewer.ftl +0 -63
- package/static/assets/pdfjs/web/locale/te/viewer.ftl +0 -60
- package/static/assets/pdfjs/web/locale/tg/viewer.ftl +38 -0
- package/static/assets/pdfjs/web/locale/tl/viewer.ftl +0 -63
- package/static/assets/pdfjs/web/locale/tr/viewer.ftl +40 -2
- package/static/assets/pdfjs/web/locale/trs/viewer.ftl +0 -72
- package/static/assets/pdfjs/web/locale/ur/viewer.ftl +0 -60
- package/static/assets/pdfjs/web/locale/uz/viewer.ftl +0 -71
- package/static/assets/pdfjs/web/locale/vi/viewer.ftl +38 -0
- package/static/assets/pdfjs/web/locale/wo/viewer.ftl +0 -77
- package/static/assets/pdfjs/web/locale/xh/viewer.ftl +0 -71
- package/static/assets/pdfjs/web/locale/zh-CN/viewer.ftl +38 -0
- package/static/assets/pdfjs/web/locale/zh-TW/viewer.ftl +38 -0
- package/static/assets/pdfjs/web/viewer.css +649 -120
- package/static/assets/pdfjs/web/viewer.html +19 -0
- package/static/assets/pdfjs/web/viewer.mjs +489 -38
- package/static/assets/pdfjs/web/viewer.mjs.map +1 -1
- package/static/chunk-22EANI6R.js +1 -0
- package/static/{chunk-KFM544CA.js → chunk-2UWN7IQF.js} +1 -1
- package/static/{chunk-N3T57OCA.js → chunk-2VSPDSJS.js} +1 -1
- package/static/{chunk-HUWQHCUX.js → chunk-34UZ7SYI.js} +1 -1
- package/static/{chunk-MWFRZBJD.js → chunk-45UQJGGY.js} +1 -1
- package/static/{chunk-LYTD6AJE.js → chunk-5TEXH3LJ.js} +1 -1
- package/static/{chunk-4KESSWTF.js → chunk-66FMKVJX.js} +1 -1
- package/static/{chunk-XE5YHU5J.js → chunk-BIUNUYZ5.js} +1 -1
- package/static/chunk-CK4BY2NX.js +27 -0
- package/static/{chunk-QTW62OKJ.js → chunk-CSBDAY77.js} +1 -1
- package/static/{chunk-XUZSYWRF.js → chunk-CXXPLBDZ.js} +1 -1
- package/static/{chunk-ZTXJC5IC.js → chunk-EILQG525.js} +1 -1
- package/static/{chunk-FJFNDK67.js → chunk-ENWABUR4.js} +1 -1
- package/static/{chunk-WL65GYD5.js → chunk-FR4AOLYL.js} +4 -4
- package/static/chunk-HW2H3ISM.js +559 -0
- package/static/{chunk-BW5PQAKK.js → chunk-HYMDGBZL.js} +1 -1
- package/static/{chunk-WLPYIJFI.js → chunk-IML5UYQG.js} +1 -1
- package/static/{chunk-Z5X7LVMZ.js → chunk-IPSMJHMQ.js} +1 -1
- package/static/{chunk-3S4WNZ2T.js → chunk-JVCWYSNP.js} +1 -1
- package/static/{chunk-CLSVDV7J.js → chunk-KGPCIUD2.js} +1 -1
- package/static/{chunk-O4AQBQBF.js → chunk-KQZJSEM3.js} +1 -1
- package/static/{chunk-MK7WZG3F.js → chunk-NPEMJJIU.js} +1 -1
- package/static/{chunk-4TEHM3AS.js → chunk-OEFBC4GG.js} +1 -1
- package/static/{chunk-O67RFAWU.js → chunk-P734A3XZ.js} +1 -1
- package/static/{chunk-SRLMFJ7C.js → chunk-RASR4CK6.js} +1 -1
- package/static/{chunk-S5WXHO6D.js → chunk-RFMOUC22.js} +1 -1
- package/static/{chunk-TTQ37MUV.js → chunk-RSS6GYNE.js} +1 -1
- package/static/{chunk-3FX6ISDY.js → chunk-SBOQGGZX.js} +1 -1
- package/static/{chunk-NV2MEIWP.js → chunk-SJAFPXQV.js} +1 -1
- package/static/{chunk-PYSFXLMV.js → chunk-XTYGMF2V.js} +1 -1
- package/static/{chunk-ZFKCGL6X.js → chunk-YCWMV2YR.js} +1 -1
- package/static/{chunk-LB7B5RIV.js → chunk-YGD22MWQ.js} +1 -1
- package/static/{chunk-MTRNPGS4.js → chunk-ZC5NIT55.js} +1 -1
- package/static/{chunk-SKDQM65G.js → chunk-ZVY37DKS.js} +1 -1
- package/static/index.html +2 -2
- package/static/main-N5CZRHAO.js +7 -0
- package/static/styles-FYUSO6OJ.css +1 -0
- package/static/chunk-AY2GOSJ2.js +0 -24
- package/static/chunk-RSNLYAN6.js +0 -560
- package/static/chunk-ZZ3LHYOY.js +0 -1
- package/static/main-RREKR34B.js +0 -10
- package/static/styles-3DONJ2Z4.css +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../backend/src/applications/sync/services/sync-paths-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 { Test, TestingModule } from '@nestjs/testing'\nimport { ContextManager } from '../../../infrastructure/context/services/context-manager.service'\nimport { FilesQueries } from '../../files/services/files-queries.service'\nimport { NotificationsManager } from '../../notifications/services/notifications-manager.service'\nimport { SpacesManager } from '../../spaces/services/spaces-manager.service'\nimport { UsersQueries } from '../../users/services/users-queries.service'\nimport { SyncPathsManager } from './sync-paths-manager.service'\nimport { SyncQueries } from './sync-queries.service'\n\ndescribe(SyncPathsManager.name, () => {\n let service: SyncPathsManager\n\n beforeAll(async () => {\n const module: TestingModule = await Test.createTestingModule({\n providers: [\n SyncPathsManager,\n { provide: ContextManager, useValue: {} },\n { provide: SpacesManager, useValue: {} },\n { provide: UsersQueries, useValue: {} },\n { provide: FilesQueries, useValue: {} },\n { provide: NotificationsManager, useValue: {} },\n {\n provide: SyncQueries,\n useValue: {}\n }\n ]\n }).compile()\n\n service = module.get<SyncPathsManager>(SyncPathsManager)\n })\n\n it('should be defined', () => {\n expect(service).toBeDefined()\n })\n})\n"],"names":["describe","SyncPathsManager","name","service","beforeAll","module","Test","createTestingModule","providers","provide","ContextManager","useValue","SpacesManager","UsersQueries","FilesQueries","NotificationsManager","SyncQueries","compile","get","it","expect","toBeDefined"],"mappings":"AAAA;;;;CAIC;;;;yBAEmC;uCACL;qCACF;6CACQ;sCACP;qCACD;yCACI;oCACL;AAE5BA,SAASC,yCAAgB,CAACC,IAAI,EAAE;IAC9B,IAAIC;IAEJC,UAAU;QACR,MAAMC,SAAwB,MAAMC,aAAI,CAACC,mBAAmB,CAAC;YAC3DC,WAAW;gBACTP,yCAAgB;gBAChB;oBAAEQ,SAASC,qCAAc;oBAAEC,UAAU,CAAC;gBAAE;gBACxC;oBAAEF,SAASG,mCAAa;oBAAED,UAAU,CAAC;gBAAE;gBACvC;oBAAEF,SAASI,iCAAY;oBAAEF,UAAU,CAAC;gBAAE;gBACtC;oBAAEF,SAASK,iCAAY;oBAAEH,UAAU,CAAC;gBAAE;gBACtC;oBAAEF,SAASM,iDAAoB;oBAAEJ,UAAU,CAAC;gBAAE;gBAC9C;oBACEF,SAASO,+BAAW;oBACpBL,UAAU,CAAC;gBACb;aACD;QACH,GAAGM,OAAO;QAEVd,UAAUE,OAAOa,GAAG,CAAmBjB,yCAAgB;IACzD;IAEAkB,GAAG,qBAAqB;QACtBC,OAAOjB,SAASkB,WAAW;IAC7B;AACF"}
|
|
1
|
+
{"version":3,"sources":["../../../../../backend/src/applications/sync/services/sync-paths-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 { HttpStatus } from '@nestjs/common'\nimport { Test, TestingModule } from '@nestjs/testing'\nimport { currentTimeStamp } from '../../../common/shared'\nimport { ContextManager } from '../../../infrastructure/context/services/context-manager.service'\nimport { FilesQueries } from '../../files/services/files-queries.service'\nimport { getProps, isPathExists, isPathIsDir } from '../../files/utils/files'\nimport { NotificationsManager } from '../../notifications/services/notifications-manager.service'\nimport { SpacesManager } from '../../spaces/services/spaces-manager.service'\nimport { getEnvPermissions } from '../../spaces/utils/permissions'\nimport { UsersQueries } from '../../users/services/users-queries.service'\nimport { SyncPathsManager } from './sync-paths-manager.service'\nimport { SyncQueries } from './sync-queries.service'\n\n// Mock modules used directly inside SyncPathsManager\njest.mock('../../../common/shared', () => ({\n currentTimeStamp: jest.fn(() => 1000)\n}))\njest.mock('../../files/utils/files', () => ({\n isPathExists: jest.fn(),\n isPathIsDir: jest.fn(),\n getProps: jest.fn(),\n sanitizePath: jest.fn((p: string) => p)\n}))\njest.mock('../../spaces/utils/permissions', () => ({\n getEnvPermissions: jest.fn(() => 'server-perms')\n}))\njest.mock('../constants/sync', () => ({\n SYNC_PATH_REPOSITORY: {\n SPACES: ['spaces'],\n PERSONAL: ['personal'],\n SHARES: ['shares']\n }\n}))\njest.mock('../../notifications/constants/notifications', () => ({\n NOTIFICATION_APP: { SYNC: 'SYNC' },\n NOTIFICATION_APP_EVENT: { SYNC: { DELETE: 'DELETE', CREATE: 'CREATE', UPDATE: 'UPDATE' } }\n}))\n\ndescribe(SyncPathsManager.name, () => {\n let service: SyncPathsManager\n let contextManager: { get: jest.Mock }\n let spacesManager: { spaceEnv: jest.Mock }\n let usersQueries: Record<string, jest.Mock>\n let filesQueries: { getSpaceFileId: jest.Mock; getOrCreateSpaceFile: jest.Mock }\n let notificationsManager: { create: jest.Mock }\n let syncQueries: {\n getClient: jest.Mock\n clientExistsForOwner: jest.Mock\n createPath: jest.Mock\n deletePath: jest.Mock\n getPaths: jest.Mock\n getPathSettings: jest.Mock\n updatePathSettings: jest.Mock\n clearCachePathSettings: jest.Mock\n }\n\n const userWith = (clientId?: string) => ({ id: 1, clientId })\n const flush = () => new Promise((r) => setImmediate(r))\n\n beforeEach(async () => {\n contextManager = { get: jest.fn(() => 'http://origin.local') }\n spacesManager = { spaceEnv: jest.fn() }\n usersQueries = {}\n filesQueries = {\n getSpaceFileId: jest.fn(),\n getOrCreateSpaceFile: jest.fn()\n }\n notificationsManager = {\n create: jest.fn().mockResolvedValue(undefined)\n }\n syncQueries = {\n getClient: jest.fn(),\n clientExistsForOwner: jest.fn(),\n createPath: jest.fn(),\n deletePath: jest.fn(),\n getPaths: jest.fn(),\n getPathSettings: jest.fn(),\n updatePathSettings: jest.fn().mockResolvedValue(undefined),\n clearCachePathSettings: jest.fn()\n }\n ;(isPathExists as jest.Mock).mockReset()\n ;(isPathIsDir as jest.Mock).mockReset()\n ;(getProps as jest.Mock).mockReset()\n ;(getEnvPermissions as jest.Mock).mockReset().mockReturnValue('server-perms')\n ;(currentTimeStamp as jest.Mock).mockReset().mockReturnValue(1000)\n\n const module: TestingModule = await Test.createTestingModule({\n providers: [\n SyncPathsManager,\n { provide: ContextManager, useValue: contextManager },\n { provide: SpacesManager, useValue: spacesManager },\n { provide: UsersQueries, useValue: usersQueries },\n { provide: FilesQueries, useValue: filesQueries },\n { provide: NotificationsManager, useValue: notificationsManager },\n { provide: SyncQueries, useValue: syncQueries }\n ]\n }).compile()\n\n module.useLogger(['fatal'])\n service = module.get<SyncPathsManager>(SyncPathsManager)\n })\n\n it('should be defined', () => {\n expect(service).toBeDefined()\n })\n\n describe('createPath', () => {\n const baseReq = () =>\n ({\n user: { id: 1, clientId: 'client-1' },\n params: { '*': 'SPACES/alias/sub' },\n space: {\n realPath: '/real/path',\n quotaIsExceeded: false,\n root: { id: 1, alias: 'alias' },\n inFilesRepository: true,\n paths: ['sub'],\n dbFile: { ownerId: 1, path: '.' },\n id: 10\n }\n }) as any\n\n it('should throw BAD_REQUEST when client id is missing', async () => {\n const req = baseReq()\n req.user.clientId = undefined\n await expect(service.createPath(req, { remotePath: 'x' } as any)).rejects.toMatchObject({\n status: HttpStatus.BAD_REQUEST,\n message: 'Client id is missing'\n })\n })\n\n it('should throw INSUFFICIENT_STORAGE when space quota is exceeded', async () => {\n const req = baseReq()\n req.space.quotaIsExceeded = true\n await expect(service.createPath(req, { remotePath: 'x' } as any)).rejects.toMatchObject({\n status: HttpStatus.INSUFFICIENT_STORAGE\n })\n })\n\n it.each([\n {\n title: 'NOT_FOUND when remote path does not exist',\n setup: () => (isPathExists as jest.Mock).mockResolvedValue(false),\n expected: { status: HttpStatus.NOT_FOUND, message: 'Remote path not found : client/remote' }\n },\n {\n title: 'BAD_REQUEST when remote path is not a directory',\n setup: () => ((isPathExists as jest.Mock).mockResolvedValue(true), (isPathIsDir as jest.Mock).mockResolvedValue(false)),\n expected: { status: HttpStatus.BAD_REQUEST, message: 'Remote path must be a directory' }\n },\n {\n title: 'NOT_FOUND when client is not found',\n setup: () => (\n (isPathExists as jest.Mock).mockResolvedValue(true),\n (isPathIsDir as jest.Mock).mockResolvedValue(true),\n syncQueries.getClient.mockResolvedValue(null)\n ),\n expected: { status: HttpStatus.NOT_FOUND, message: 'Client not found' }\n }\n ])('should throw $title', async ({ setup, expected }) => {\n const req = baseReq()\n setup()\n await expect(service.createPath(req, { remotePath: 'client/remote' } as any)).rejects.toMatchObject(expected)\n })\n\n it('should create path and return id and permissions, overriding remotePath and permissions', async () => {\n const req = baseReq()\n ;(isPathExists as jest.Mock).mockResolvedValue(true)\n ;(isPathIsDir as jest.Mock).mockResolvedValue(true)\n ;(getEnvPermissions as jest.Mock).mockReturnValue('env-perms')\n syncQueries.getClient.mockResolvedValue({ id: 'client-1' })\n // Spy on private getDBProps to simplify\n const getDBPropsSpy = jest.spyOn<any, any>(service as any, 'getDBProps').mockResolvedValue({ ownerId: 1 })\n syncQueries.createPath.mockResolvedValue(123)\n\n const res = await service.createPath(req, { remotePath: 'client/remote', permissions: 'client-perms' } as any)\n expect(res).toEqual({ id: 123, permissions: 'env-perms' })\n expect(syncQueries.createPath).toHaveBeenCalledWith(\n 'client-1',\n { ownerId: 1 },\n expect.objectContaining({ remotePath: 'SPACES/alias/sub', permissions: 'env-perms' })\n )\n\n getDBPropsSpy.mockRestore()\n })\n })\n\n describe('deletePath', () => {\n it.each([\n { user: { id: 1, clientId: undefined } as any, id: 10, status: HttpStatus.BAD_REQUEST, msg: 'Client id is missing' },\n { user: { id: 1, clientId: 'c1' } as any, id: 10, status: HttpStatus.FORBIDDEN }\n ])('should handle errors (status=$status)', async ({ user, id, status, msg }) => {\n if (status === HttpStatus.FORBIDDEN) syncQueries.clientExistsForOwner.mockResolvedValue(false)\n await expect(service.deletePath(user, id)).rejects.toMatchObject(msg ? { status, message: msg } : { status })\n })\n\n it('should catch errors from deletePath and throw BAD_REQUEST', async () => {\n const user: any = { id: 1, clientId: 'c1' }\n syncQueries.clientExistsForOwner.mockResolvedValue(true)\n syncQueries.deletePath.mockRejectedValue(new Error('db'))\n await expect(service.deletePath(user, 10)).rejects.toMatchObject({\n status: HttpStatus.BAD_REQUEST,\n message: 'Unable to remove path'\n })\n expect(syncQueries.deletePath).toHaveBeenCalledWith('c1', 10)\n })\n\n it('should delete path successfully when allowed', async () => {\n const user: any = { id: 1, clientId: undefined }\n syncQueries.clientExistsForOwner.mockResolvedValue(true)\n syncQueries.deletePath.mockResolvedValue(undefined)\n await expect(service.deletePath(user, 10, 'cX')).resolves.toBeUndefined()\n expect(syncQueries.deletePath).toHaveBeenCalledWith('cX', 10)\n })\n })\n\n describe('updatePath', () => {\n it('should throw FORBIDDEN when client does not belong to owner', async () => {\n syncQueries.clientExistsForOwner.mockResolvedValue(false)\n await expect(service.updatePath({ id: 1 } as any, 'c1', 5, {} as any)).rejects.toMatchObject({\n status: HttpStatus.FORBIDDEN\n })\n })\n\n it('should throw NOT_FOUND when path settings do not exist', async () => {\n syncQueries.clientExistsForOwner.mockResolvedValue(true)\n syncQueries.getPathSettings.mockResolvedValue(null)\n await expect(service.updatePath({ id: 1 } as any, 'c1', 5, {} as any)).rejects.toMatchObject({\n status: HttpStatus.NOT_FOUND,\n message: 'Sync path not found'\n })\n })\n\n it('should update path settings, set new timestamp, clear cache and return updated settings', async () => {\n syncQueries.clientExistsForOwner.mockResolvedValue(true)\n syncQueries.getPathSettings.mockResolvedValue({ id: 5, timestamp: 500, lastSync: 1, remotePath: '/a', permissions: 'p' })\n syncQueries.updatePathSettings.mockResolvedValue(undefined)\n ;(currentTimeStamp as jest.Mock).mockReturnValue(4242)\n\n const out = await service.updatePath({ id: 1 } as any, 'c1', 5, { id: 666, lastSync: 3, permissions: 'new' } as any)\n expect(syncQueries.updatePathSettings).toHaveBeenCalledWith(\n 'c1',\n 5,\n expect.objectContaining({ id: 5, lastSync: 3, timestamp: 4242, permissions: 'new' })\n )\n expect(syncQueries.clearCachePathSettings).toHaveBeenCalledWith('c1', 5)\n expect(out).toEqual(expect.objectContaining({ id: 5, lastSync: 3, timestamp: 4242, permissions: 'new' }))\n })\n\n it('should clear cache and throw INTERNAL_SERVER_ERROR when update fails', async () => {\n syncQueries.clientExistsForOwner.mockResolvedValue(true)\n syncQueries.getPathSettings.mockResolvedValue({ id: 5, timestamp: 500, lastSync: 1 })\n syncQueries.updatePathSettings.mockRejectedValue(new Error('db'))\n await expect(service.updatePath({ id: 1 } as any, 'c1', 5, {} as any)).rejects.toMatchObject({\n status: HttpStatus.INTERNAL_SERVER_ERROR,\n message: 'Unable to update path'\n })\n expect(syncQueries.clearCachePathSettings).toHaveBeenCalledWith('c1', 5)\n })\n })\n\n describe('updatePaths', () => {\n beforeEach(() => {\n syncQueries.clientExistsForOwner.mockResolvedValue(true)\n spacesManager.spaceEnv.mockResolvedValue({ envPermissions: 'p' })\n })\n\n it('should throw when client id is missing', async () => {\n await expect(service.updatePaths(userWith(undefined) as any, [])).rejects.toMatchObject({\n status: HttpStatus.BAD_REQUEST,\n message: 'Client id is missing'\n })\n })\n\n it('should throw FORBIDDEN when client does not belong to owner', async () => {\n syncQueries.clientExistsForOwner.mockResolvedValue(false)\n await expect(service.updatePaths(userWith('c1') as any, [])).rejects.toMatchObject({\n status: HttpStatus.FORBIDDEN\n })\n })\n\n it('should mark client paths as deleted and notify when no corresponding server paths (server remotePath undefined)', async () => {\n syncQueries.clientExistsForOwner.mockResolvedValue(true)\n syncQueries.getPaths.mockResolvedValue([{ id: 2, settings: { timestamp: 1, lastSync: 1 }, remotePath: undefined }])\n const clientPaths = [{ id: 1, remotePath: 'SPACES/a', timestamp: 1, lastSync: 1, permissions: 'p' } as any]\n\n const res = await service.updatePaths(userWith('c1') as any, clientPaths)\n expect(res.delete).toEqual([1])\n expect(notificationsManager.create).toHaveBeenCalledTimes(1)\n })\n\n it('should propagate spaceEnv errors as BAD_REQUEST', async () => {\n syncQueries.clientExistsForOwner.mockResolvedValue(true)\n syncQueries.getPaths.mockResolvedValue([{ id: 1, settings: { timestamp: 1, lastSync: 1 }, remotePath: 'SPACES/x' }])\n spacesManager.spaceEnv.mockRejectedValue(new Error('boom'))\n await expect(\n service.updatePaths(userWith('c1') as any, [{ id: 1, remotePath: 'SPACES/x', timestamp: 1, lastSync: 1, permissions: 'p' } as any])\n ).rejects.toMatchObject({\n status: HttpStatus.BAD_REQUEST,\n message: 'boom'\n })\n })\n\n it('should skip server path when space is null and mark client item as deleted', async () => {\n syncQueries.clientExistsForOwner.mockResolvedValue(true)\n syncQueries.getPaths.mockResolvedValue([{ id: 2, settings: { timestamp: 1, lastSync: 1 }, remotePath: 'SPACES/x' }])\n spacesManager.spaceEnv.mockResolvedValue(null)\n const res = await service.updatePaths(userWith('c1') as any, [\n { id: 2, remotePath: 'SPACES/x', timestamp: 1, lastSync: 1, permissions: 'p' } as any\n ])\n expect(res.delete).toEqual([2])\n expect(notificationsManager.create).toHaveBeenCalledTimes(1)\n })\n\n it('should add server-only path to client with server permissions', async () => {\n syncQueries.clientExistsForOwner.mockResolvedValue(true)\n syncQueries.getPaths.mockResolvedValue([{ id: 3, settings: { timestamp: 1, lastSync: 1 }, remotePath: 'SPACES/x' }])\n spacesManager.spaceEnv.mockResolvedValue({ envPermissions: 'perm-xyz' })\n const res = await service.updatePaths(userWith('c1') as any, [])\n expect(res.add).toHaveLength(1)\n expect(res.add[0]).toEqual(expect.objectContaining({ id: 3, remotePath: 'SPACES/x', permissions: 'perm-xyz' }))\n })\n\n it('should update server settings from client when client is newer (no hasUpdates)', async () => {\n syncQueries.clientExistsForOwner.mockResolvedValue(true)\n syncQueries.getPaths.mockResolvedValue([{ id: 5, settings: { timestamp: 1, lastSync: 1, foo: 'server' }, remotePath: 'SPACES/x' }])\n spacesManager.spaceEnv.mockResolvedValue({ envPermissions: 'p' })\n\n const client = [{ id: 5, timestamp: 10, lastSync: 2, remotePath: 'SPACES/x', permissions: 'p', foo: 'client' } as any]\n await service.updatePaths(userWith('c1') as any, client)\n\n expect(syncQueries.updatePathSettings).toHaveBeenCalledWith('c1', 5, expect.objectContaining({ foo: 'client', lastSync: 2 }))\n // No client update instructions because hasUpdates=false and serverNewer=false\n })\n\n it('should push server-newer updates to client and also remotePath/permissions corrections', async () => {\n syncQueries.clientExistsForOwner.mockResolvedValue(true)\n syncQueries.getPaths.mockResolvedValue([{ id: 7, settings: { timestamp: 20, lastSync: 5, srv: true }, remotePath: 'SPACES/correct' }])\n spacesManager.spaceEnv.mockResolvedValue({ envPermissions: 'permX' })\n\n const client = [{ id: 7, timestamp: 10, lastSync: 5, remotePath: 'SPACES/wrong', permissions: 'old' } as any]\n const res = await service.updatePaths(userWith('c1') as any, client)\n\n // Should have two client updates: one full server settings + corrections, and one corrections-only\n expect(res.update).toEqual(\n expect.arrayContaining([\n expect.objectContaining({ id: 7, srv: true, remotePath: 'SPACES/correct', permissions: 'permX' }),\n expect.objectContaining({ id: 7, remotePath: 'SPACES/correct', permissions: 'permX' })\n ])\n )\n expect(syncQueries.updatePathSettings).toHaveBeenCalledWith('c1', 7, expect.objectContaining({ lastSync: 5 }))\n // clear cache called for each update instruction\n expect(syncQueries.clearCachePathSettings).toHaveBeenCalledTimes(res.update.length)\n for (const u of res.update) {\n expect(syncQueries.clearCachePathSettings).toHaveBeenCalledWith('c1', u.id)\n }\n })\n\n it('should trigger update when lastSync differs even if timestamps and settings match', async () => {\n syncQueries.clientExistsForOwner.mockResolvedValue(true)\n syncQueries.getPaths.mockResolvedValue([{ id: 9, settings: { timestamp: 10, lastSync: 1, flag: 'S' }, remotePath: 'SPACES/x' }])\n spacesManager.spaceEnv.mockResolvedValue({ envPermissions: 'p' })\n\n const client = [{ id: 9, timestamp: 10, lastSync: 2, remotePath: 'SPACES/x', permissions: 'p', flag: 'S' } as any]\n await service.updatePaths(userWith('c1') as any, client)\n expect(syncQueries.updatePathSettings).toHaveBeenCalledWith('c1', 9, expect.objectContaining({ lastSync: 2 }))\n })\n\n it('should perform no changes when client and server are identical (no-op branch)', async () => {\n syncQueries.clientExistsForOwner.mockResolvedValue(true)\n syncQueries.getPaths.mockResolvedValue([{ id: 12, settings: { timestamp: 5, lastSync: 7, flag: 'A' }, remotePath: 'SPACES/x' }])\n spacesManager.spaceEnv.mockResolvedValue({ envPermissions: 'p' })\n\n const client = [{ id: 12, timestamp: 5, lastSync: 7, remotePath: 'SPACES/x', permissions: 'p', flag: 'A' } as any]\n const res = await service.updatePaths(userWith('c1') as any, client)\n\n expect(res).toEqual({ add: [], update: [], delete: [] })\n expect(syncQueries.updatePathSettings).not.toHaveBeenCalled()\n })\n\n it('should correct client info while keeping server settings when timestamps are equal (hasUpdates=true)', async () => {\n syncQueries.clientExistsForOwner.mockResolvedValue(true)\n // Server has srv='S', client has wrong remotePath/permissions and extra clientOnly flag\n syncQueries.getPaths.mockResolvedValue([{ id: 13, settings: { timestamp: 5, lastSync: 1, srv: 'S' }, remotePath: 'SPACES/correct' }])\n spacesManager.spaceEnv.mockResolvedValue({ envPermissions: 'permX' })\n\n const client = [{ id: 13, timestamp: 5, lastSync: 1, remotePath: 'SPACES/wrong', permissions: 'old', clientOnly: 'C' } as any]\n const res = await service.updatePaths(userWith('c1') as any, client)\n\n // Server uses its own settings base (srv: 'S') with corrections applied\n expect(syncQueries.updatePathSettings).toHaveBeenCalledWith(\n 'c1',\n 13,\n expect.objectContaining({ srv: 'S', lastSync: 1, remotePath: 'SPACES/correct', permissions: 'permX' })\n )\n // Client should receive corrections for remotePath and permissions\n expect(res.update).toEqual(expect.arrayContaining([expect.objectContaining({ id: 13, remotePath: 'SPACES/correct', permissions: 'permX' })]))\n })\n\n it('should log error when updatePathSettings rejects inside updatePaths', async () => {\n syncQueries.clientExistsForOwner.mockResolvedValue(true)\n syncQueries.getPaths.mockResolvedValue([{ id: 11, settings: { timestamp: 1, lastSync: 1 }, remotePath: 'SPACES/x' }])\n spacesManager.spaceEnv.mockResolvedValue({ envPermissions: 'p' })\n // Force client newer to trigger updatePathSettings\n const client = [{ id: 11, timestamp: 10, lastSync: 2, remotePath: 'SPACES/x', permissions: 'p' } as any]\n syncQueries.updatePathSettings.mockRejectedValueOnce(new Error('db-fail'))\n const loggerSpy = jest.spyOn((service as any).logger, 'error').mockImplementation(() => undefined)\n\n await service.updatePaths(userWith('c1') as any, client)\n // wait for microtasks to ensure .catch executed\n await flush()\n\n expect(loggerSpy).toHaveBeenCalled()\n expect(loggerSpy.mock.calls.some(([msg]) => String(msg).includes('updatePaths'))).toBe(true)\n })\n\n it('should catch notify failure at updatePaths level when building notification fails', async () => {\n syncQueries.clientExistsForOwner.mockResolvedValue(true)\n // No server paths: will mark client path as deleted and call notify\n syncQueries.getPaths.mockResolvedValue([])\n const loggerSpy = jest.spyOn((service as any).logger, 'error').mockImplementation(() => undefined)\n // BAD first segment to make notify fail while building URL (before notificationsManager.create)\n const client = [{ id: 1, remotePath: 'BAD/a/b', timestamp: 1, lastSync: 1, permissions: 'p' } as any]\n\n const res = await service.updatePaths(userWith('c1') as any, client)\n await flush()\n\n expect(res.delete).toEqual([1])\n expect(loggerSpy).toHaveBeenCalled()\n expect(loggerSpy.mock.calls.some(([msg]) => String(msg).includes('updatePaths'))).toBe(true)\n // create not called because we failed before reaching it\n expect(notificationsManager.create).not.toHaveBeenCalled()\n })\n\n it('should catch error inside notify when notifications creation fails', async () => {\n syncQueries.clientExistsForOwner.mockResolvedValue(true)\n syncQueries.getPaths.mockResolvedValue([])\n // Valid remotePath to build URL correctly\n const client = [{ id: 2, remotePath: 'SPACES/a', timestamp: 1, lastSync: 1, permissions: 'p' } as any]\n const loggerSpy = jest.spyOn((service as any).logger, 'error').mockImplementation(() => undefined)\n notificationsManager.create.mockRejectedValueOnce(new Error('notify-fail'))\n\n const res = await service.updatePaths(userWith('c1') as any, client)\n await flush()\n\n expect(res.delete).toEqual([2])\n expect(loggerSpy).toHaveBeenCalled()\n // error comes from notify() catch\n expect(loggerSpy.mock.calls.some(([msg]) => String(msg).includes('notify'))).toBe(true)\n })\n })\n\n describe('getDBProps (private) branches', () => {\n it('should throw BAD_REQUEST for shares list selection', async () => {\n await expect((service as any).getDBProps({ inSharesList: true } as any)).rejects.toMatchObject({\n status: HttpStatus.BAD_REQUEST,\n message: 'Sync all shares is not supported, you must select a sub-directory'\n })\n })\n\n it('should return ownerId only for personal space at root', async () => {\n const res = await (service as any).getDBProps({\n inSharesList: false,\n inPersonalSpace: true,\n paths: [],\n dbFile: { ownerId: 42 }\n } as any)\n expect(res).toEqual({ ownerId: 42 })\n })\n\n it('should return ownerId and fileId for personal space subdir', async () => {\n const getOrCreateFileIdSpy = jest.spyOn<any, any>(service as any, 'getOrCreateFileId').mockResolvedValue(77)\n const res = await (service as any).getDBProps({\n inPersonalSpace: true,\n paths: ['sub'],\n dbFile: { ownerId: 42 }\n } as any)\n expect(res).toEqual({ ownerId: 42, fileId: 77 })\n getOrCreateFileIdSpy.mockRestore()\n })\n\n it('should throw BAD_REQUEST for whole files repository without alias', async () => {\n await expect((service as any).getDBProps({ inFilesRepository: true, root: { alias: null }, paths: [] } as any)).rejects.toMatchObject({\n status: HttpStatus.BAD_REQUEST,\n message: 'Sync all space is not yet supported, you must select a sub-directory'\n })\n })\n\n it('should return spaceId and rootId for files repository root selection', async () => {\n const res = await (service as any).getDBProps({\n inFilesRepository: true,\n id: 5,\n root: { id: 3, alias: 'x' },\n paths: []\n } as any)\n expect(res).toEqual({ spaceId: 5, spaceRootId: 3 })\n })\n\n it('should return spaceId, rootId and fileId for files repository subdir or null root', async () => {\n const getOrCreateFileIdSpy = jest.spyOn<any, any>(service as any, 'getOrCreateFileId').mockResolvedValue(88)\n const res = await (service as any).getDBProps({\n inFilesRepository: true,\n id: 5,\n root: { id: 3, alias: 'x' },\n paths: ['sub']\n } as any)\n expect(res).toEqual({ spaceId: 5, spaceRootId: 3, fileId: 88 })\n getOrCreateFileIdSpy.mockRestore()\n })\n\n it('should return spaceId and null spaceRootId plus fileId when files repository root has no id', async () => {\n const getOrCreateFileIdSpy = jest.spyOn<any, any>(service as any, 'getOrCreateFileId').mockResolvedValue(90)\n const res = await (service as any).getDBProps({\n inFilesRepository: true,\n id: 6,\n root: { id: undefined, alias: 'x' },\n paths: []\n } as any)\n expect(res).toEqual({ spaceId: 6, spaceRootId: null, fileId: 90 })\n getOrCreateFileIdSpy.mockRestore()\n })\n\n it('should return shareId only for shares repository root', async () => {\n const res = await (service as any).getDBProps({\n inSharesList: false,\n inPersonalSpace: false,\n inFilesRepository: false,\n inSharesRepository: true,\n id: 9,\n paths: []\n } as any)\n expect(res).toEqual({ shareId: 9 })\n })\n\n it('should return shareId and fileId for shares repository subdir', async () => {\n const getOrCreateFileIdSpy = jest.spyOn<any, any>(service as any, 'getOrCreateFileId').mockResolvedValue(55)\n const res = await (service as any).getDBProps({\n inSharesList: false,\n inPersonalSpace: false,\n inFilesRepository: false,\n inSharesRepository: true,\n id: 9,\n paths: ['sub']\n } as any)\n expect(res).toEqual({ shareId: 9, fileId: 55 })\n getOrCreateFileIdSpy.mockRestore()\n })\n\n it('should return undefined when no space flags match (no branch taken)', async () => {\n const res = await (service as any).getDBProps({\n inSharesList: false,\n inPersonalSpace: false,\n inFilesRepository: false,\n inSharesRepository: false,\n paths: []\n } as any)\n expect(res).toBeUndefined()\n })\n })\n\n describe('getOrCreateFileId (private) branches', () => {\n it('should return existing file id without creation', async () => {\n ;(getProps as jest.Mock).mockResolvedValue({ name: 'file' })\n filesQueries.getSpaceFileId.mockResolvedValue(101)\n const id = await (service as any).getOrCreateFileId({\n realPath: '/rp',\n dbFile: { path: '.' }\n })\n expect(id).toBe(101)\n expect(filesQueries.getOrCreateSpaceFile).not.toHaveBeenCalled()\n })\n\n it('should create file when not exists and return its id', async () => {\n ;(getProps as jest.Mock).mockResolvedValue({ id: 999, name: 'file' })\n filesQueries.getSpaceFileId.mockResolvedValue(0)\n filesQueries.getOrCreateSpaceFile.mockResolvedValue(202)\n const id = await (service as any).getOrCreateFileId({\n realPath: '/rp',\n dbFile: { path: '.' }\n })\n expect(id).toBe(202)\n expect(filesQueries.getOrCreateSpaceFile).toHaveBeenCalledWith(0, expect.objectContaining({ id: undefined }), { path: '.' })\n })\n })\n})\n"],"names":["jest","mock","currentTimeStamp","fn","isPathExists","isPathIsDir","getProps","sanitizePath","p","getEnvPermissions","SYNC_PATH_REPOSITORY","SPACES","PERSONAL","SHARES","NOTIFICATION_APP","SYNC","NOTIFICATION_APP_EVENT","DELETE","CREATE","UPDATE","describe","SyncPathsManager","name","service","contextManager","spacesManager","usersQueries","filesQueries","notificationsManager","syncQueries","userWith","clientId","id","flush","Promise","r","setImmediate","beforeEach","get","spaceEnv","getSpaceFileId","getOrCreateSpaceFile","create","mockResolvedValue","undefined","getClient","clientExistsForOwner","createPath","deletePath","getPaths","getPathSettings","updatePathSettings","clearCachePathSettings","mockReset","mockReturnValue","module","Test","createTestingModule","providers","provide","ContextManager","useValue","SpacesManager","UsersQueries","FilesQueries","NotificationsManager","SyncQueries","compile","useLogger","it","expect","toBeDefined","baseReq","user","params","space","realPath","quotaIsExceeded","root","alias","inFilesRepository","paths","dbFile","ownerId","path","req","remotePath","rejects","toMatchObject","status","HttpStatus","BAD_REQUEST","message","INSUFFICIENT_STORAGE","each","title","setup","expected","NOT_FOUND","getDBPropsSpy","spyOn","res","permissions","toEqual","toHaveBeenCalledWith","objectContaining","mockRestore","msg","FORBIDDEN","mockRejectedValue","Error","resolves","toBeUndefined","updatePath","timestamp","lastSync","out","INTERNAL_SERVER_ERROR","envPermissions","updatePaths","settings","clientPaths","delete","toHaveBeenCalledTimes","add","toHaveLength","foo","client","srv","update","arrayContaining","length","u","flag","not","toHaveBeenCalled","clientOnly","mockRejectedValueOnce","loggerSpy","logger","mockImplementation","calls","some","String","includes","toBe","getDBProps","inSharesList","inPersonalSpace","getOrCreateFileIdSpy","fileId","spaceId","spaceRootId","inSharesRepository","shareId","getOrCreateFileId"],"mappings":"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"}
|
|
@@ -17,7 +17,7 @@ const _sync = require("../constants/sync");
|
|
|
17
17
|
function SYNC_PATH_TO_SPACE_SEGMENTS(path) {
|
|
18
18
|
const urlSegments = (0, _routes.PATH_TO_SPACE_SEGMENTS)(path);
|
|
19
19
|
const repository = urlSegments.shift();
|
|
20
|
-
if (!(repository in _sync.
|
|
20
|
+
if (!(repository in _sync.SYNC_PATH_REPOSITORY)) {
|
|
21
21
|
throw new Error(`Repository not found : ${repository}`);
|
|
22
22
|
}
|
|
23
23
|
urlSegments.unshift(..._sync.SYNC_PATH_REPOSITORY[repository]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../backend/src/applications/sync/utils/routes.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 { PATH_TO_SPACE_SEGMENTS } from '../../spaces/utils/routes'\nimport { SYNC_PATH_REPOSITORY
|
|
1
|
+
{"version":3,"sources":["../../../../../backend/src/applications/sync/utils/routes.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 { PATH_TO_SPACE_SEGMENTS } from '../../spaces/utils/routes'\nimport { SYNC_PATH_REPOSITORY } from '../constants/sync'\n\nexport function SYNC_PATH_TO_SPACE_SEGMENTS(path: string): string[] {\n const urlSegments = PATH_TO_SPACE_SEGMENTS(path)\n const repository = urlSegments.shift()\n if (!(repository in SYNC_PATH_REPOSITORY)) {\n throw new Error(`Repository not found : ${repository}`)\n }\n urlSegments.unshift(...SYNC_PATH_REPOSITORY[repository])\n return urlSegments\n}\n"],"names":["SYNC_PATH_TO_SPACE_SEGMENTS","path","urlSegments","PATH_TO_SPACE_SEGMENTS","repository","shift","SYNC_PATH_REPOSITORY","Error","unshift"],"mappings":"AAAA;;;;CAIC;;;;+BAKeA;;;eAAAA;;;wBAHuB;sBACF;AAE9B,SAASA,4BAA4BC,IAAY;IACtD,MAAMC,cAAcC,IAAAA,8BAAsB,EAACF;IAC3C,MAAMG,aAAaF,YAAYG,KAAK;IACpC,IAAI,CAAED,CAAAA,cAAcE,0BAAoB,AAAD,GAAI;QACzC,MAAM,IAAIC,MAAM,CAAC,uBAAuB,EAAEH,YAAY;IACxD;IACAF,YAAYM,OAAO,IAAIF,0BAAoB,CAACF,WAAW;IACvD,OAAOF;AACT"}
|
|
@@ -30,14 +30,14 @@ let UserPermissionsGuard = class UserPermissionsGuard {
|
|
|
30
30
|
ctx.getHandler(),
|
|
31
31
|
ctx.getClass()
|
|
32
32
|
]);
|
|
33
|
-
if (typeof permissions === 'object') {
|
|
34
|
-
// used to bypass the check, the guard is called without argument, the value is '{}'
|
|
35
|
-
return true;
|
|
36
|
-
}
|
|
37
33
|
if (permissions === undefined) {
|
|
38
34
|
this.logger.warn(`no application defined on ${ctx.getClass().name}:${ctx.getHandler().name}`);
|
|
39
35
|
return false;
|
|
40
36
|
}
|
|
37
|
+
if (Object.keys(permissions).length === 0) {
|
|
38
|
+
// used to bypass the check, the guard is called without argument, the value is '{}'
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
41
|
const req = ctx.switchToHttp().getRequest();
|
|
42
42
|
if (req.user.isAdmin) {
|
|
43
43
|
return true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../backend/src/applications/users/guards/permissions.guard.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 { CanActivate, ExecutionContext, HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common'\nimport { Reflector } from '@nestjs/core'\nimport { FastifyAuthenticatedRequest } from '../../../authentication/interfaces/auth-request.interface'\nimport { USER_PERMISSION } from '../constants/user'\nimport { UserHavePermission } from '../decorators/permissions.decorator'\n\n@Injectable()\nexport class UserPermissionsGuard implements CanActivate {\n private readonly logger = new Logger(UserPermissionsGuard.name)\n\n constructor(private readonly reflector: Reflector) {}\n\n canActivate(ctx: ExecutionContext): boolean {\n const permissions: USER_PERMISSION | USER_PERMISSION[] = this.reflector.getAllAndOverride(UserHavePermission, [ctx.getHandler(), ctx.getClass()])\n if (
|
|
1
|
+
{"version":3,"sources":["../../../../../backend/src/applications/users/guards/permissions.guard.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 { CanActivate, ExecutionContext, HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common'\nimport { Reflector } from '@nestjs/core'\nimport { FastifyAuthenticatedRequest } from '../../../authentication/interfaces/auth-request.interface'\nimport { USER_PERMISSION } from '../constants/user'\nimport { UserHavePermission } from '../decorators/permissions.decorator'\n\n@Injectable()\nexport class UserPermissionsGuard implements CanActivate {\n private readonly logger = new Logger(UserPermissionsGuard.name)\n\n constructor(private readonly reflector: Reflector) {}\n\n canActivate(ctx: ExecutionContext): boolean {\n const permissions: USER_PERMISSION | USER_PERMISSION[] = this.reflector.getAllAndOverride(UserHavePermission, [ctx.getHandler(), ctx.getClass()])\n if (permissions === undefined) {\n this.logger.warn(`no application defined on ${ctx.getClass().name}:${ctx.getHandler().name}`)\n return false\n }\n if (Object.keys(permissions).length === 0) {\n // used to bypass the check, the guard is called without argument, the value is '{}'\n return true\n }\n const req: FastifyAuthenticatedRequest = ctx.switchToHttp().getRequest()\n if (req.user.isAdmin) {\n return true\n }\n let authorized = false\n if (Array.isArray(permissions)) {\n // if any of the apps are allowed, proceed\n authorized = permissions.some((p: string) => req.user.havePermission(p))\n } else {\n authorized = req.user.havePermission(permissions)\n }\n if (!authorized) {\n this.logger.warn(`does not have permissions : ${permissions}`)\n throw new HttpException('You are not allowed to do this action', HttpStatus.FORBIDDEN)\n }\n return authorized\n }\n}\n"],"names":["UserPermissionsGuard","canActivate","ctx","permissions","reflector","getAllAndOverride","UserHavePermission","getHandler","getClass","undefined","logger","warn","name","Object","keys","length","req","switchToHttp","getRequest","user","isAdmin","authorized","Array","isArray","some","p","havePermission","HttpException","HttpStatus","FORBIDDEN","Logger"],"mappings":"AAAA;;;;CAIC;;;;+BASYA;;;eAAAA;;;wBAPgF;sBACnE;sCAGS;;;;;;;;;;AAG5B,IAAA,AAAMA,uBAAN,MAAMA;IAKXC,YAAYC,GAAqB,EAAW;QAC1C,MAAMC,cAAmD,IAAI,CAACC,SAAS,CAACC,iBAAiB,CAACC,wCAAkB,EAAE;YAACJ,IAAIK,UAAU;YAAIL,IAAIM,QAAQ;SAAG;QAChJ,IAAIL,gBAAgBM,WAAW;YAC7B,IAAI,CAACC,MAAM,CAACC,IAAI,CAAC,CAAC,0BAA0B,EAAET,IAAIM,QAAQ,GAAGI,IAAI,CAAC,CAAC,EAAEV,IAAIK,UAAU,GAAGK,IAAI,EAAE;YAC5F,OAAO;QACT;QACA,IAAIC,OAAOC,IAAI,CAACX,aAAaY,MAAM,KAAK,GAAG;YACzC,oFAAoF;YACpF,OAAO;QACT;QACA,MAAMC,MAAmCd,IAAIe,YAAY,GAAGC,UAAU;QACtE,IAAIF,IAAIG,IAAI,CAACC,OAAO,EAAE;YACpB,OAAO;QACT;QACA,IAAIC,aAAa;QACjB,IAAIC,MAAMC,OAAO,CAACpB,cAAc;YAC9B,0CAA0C;YAC1CkB,aAAalB,YAAYqB,IAAI,CAAC,CAACC,IAAcT,IAAIG,IAAI,CAACO,cAAc,CAACD;QACvE,OAAO;YACLJ,aAAaL,IAAIG,IAAI,CAACO,cAAc,CAACvB;QACvC;QACA,IAAI,CAACkB,YAAY;YACf,IAAI,CAACX,MAAM,CAACC,IAAI,CAAC,CAAC,4BAA4B,EAAER,aAAa;YAC7D,MAAM,IAAIwB,qBAAa,CAAC,yCAAyCC,kBAAU,CAACC,SAAS;QACvF;QACA,OAAOR;IACT;IA5BA,YAAY,AAAiBjB,SAAoB,CAAE;aAAtBA,YAAAA;aAFZM,SAAS,IAAIoB,cAAM,CAAC9B,qBAAqBY,IAAI;IAEV;AA6BtD"}
|
|
@@ -31,7 +31,7 @@ describe(_permissionsguard.UserPermissionsGuard.name, ()=>{
|
|
|
31
31
|
expect(permissionsGuard).toBeDefined();
|
|
32
32
|
expect(userTest).toBeDefined();
|
|
33
33
|
});
|
|
34
|
-
it('should pass with a valid permission',
|
|
34
|
+
it('should pass with a valid permission', ()=>{
|
|
35
35
|
userTest.applications = [
|
|
36
36
|
_user.USER_PERMISSION.PERSONAL_SPACE
|
|
37
37
|
];
|
|
@@ -42,7 +42,7 @@ describe(_permissionsguard.UserPermissionsGuard.name, ()=>{
|
|
|
42
42
|
});
|
|
43
43
|
expect(permissionsGuard.canActivate(context)).toBe(true);
|
|
44
44
|
});
|
|
45
|
-
it('should pass if any of the permissions are granted',
|
|
45
|
+
it('should pass if any of the permissions are granted', ()=>{
|
|
46
46
|
userTest.applications = [
|
|
47
47
|
_user.USER_PERMISSION.PERSONAL_SPACE
|
|
48
48
|
];
|
|
@@ -56,7 +56,7 @@ describe(_permissionsguard.UserPermissionsGuard.name, ()=>{
|
|
|
56
56
|
});
|
|
57
57
|
expect(permissionsGuard.canActivate(context)).toBe(true);
|
|
58
58
|
});
|
|
59
|
-
it('should not pass with a bad permission',
|
|
59
|
+
it('should not pass with a bad permission', ()=>{
|
|
60
60
|
userTest.applications = [];
|
|
61
61
|
context = (0, _tsjest.createMock)();
|
|
62
62
|
(0, _permissionsdecorator.UserHavePermission)(_user.USER_PERMISSION.PERSONAL_SPACE)(context.getHandler());
|
|
@@ -65,7 +65,7 @@ describe(_permissionsguard.UserPermissionsGuard.name, ()=>{
|
|
|
65
65
|
});
|
|
66
66
|
expect(()=>permissionsGuard.canActivate(context)).toThrow(_common.HttpException);
|
|
67
67
|
});
|
|
68
|
-
it('should pass with no permissions but with the admin role',
|
|
68
|
+
it('should pass with no permissions but with the admin role', ()=>{
|
|
69
69
|
userTest.applications = [];
|
|
70
70
|
userTest.role = _user.USER_ROLE.ADMINISTRATOR;
|
|
71
71
|
context = (0, _tsjest.createMock)();
|
|
@@ -77,7 +77,7 @@ describe(_permissionsguard.UserPermissionsGuard.name, ()=>{
|
|
|
77
77
|
// reset
|
|
78
78
|
userTest.role = _user.USER_ROLE.USER;
|
|
79
79
|
});
|
|
80
|
-
it('should not pass with a missing decorator',
|
|
80
|
+
it('should not pass with a missing decorator', ()=>{
|
|
81
81
|
userTest.applications = [
|
|
82
82
|
_user.USER_PERMISSION.PERSONAL_SPACE
|
|
83
83
|
];
|
|
@@ -87,7 +87,7 @@ describe(_permissionsguard.UserPermissionsGuard.name, ()=>{
|
|
|
87
87
|
});
|
|
88
88
|
expect(permissionsGuard.canActivate(context)).toBeFalsy();
|
|
89
89
|
});
|
|
90
|
-
it('should pass with an empty decorator',
|
|
90
|
+
it('should pass with an empty decorator', ()=>{
|
|
91
91
|
userTest.applications = [
|
|
92
92
|
_user.USER_PERMISSION.PERSONAL_SPACE
|
|
93
93
|
];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../backend/src/applications/users/guards/permissions.guard.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 { createMock, DeepMocked } from '@golevelup/ts-jest'\nimport { ExecutionContext, HttpException, Logger } from '@nestjs/common'\nimport { Reflector } from '@nestjs/core'\nimport { USER_PERMISSION, USER_ROLE } from '../constants/user'\nimport { UserHavePermission } from '../decorators/permissions.decorator'\nimport { UserModel } from '../models/user.model'\nimport { generateUserTest } from '../utils/test'\nimport { UserPermissionsGuard } from './permissions.guard'\n\ndescribe(UserPermissionsGuard.name, () => {\n let reflector: Reflector\n let permissionsGuard: UserPermissionsGuard\n let userTest: UserModel\n let context: DeepMocked<ExecutionContext>\n\n beforeAll(async () => {\n reflector = new Reflector()\n permissionsGuard = new UserPermissionsGuard(reflector)\n userTest = new UserModel(generateUserTest())\n Logger.overrideLogger(['fatal'])\n })\n\n it('should be defined', () => {\n expect(permissionsGuard).toBeDefined()\n expect(userTest).toBeDefined()\n })\n\n it('should pass with a valid permission',
|
|
1
|
+
{"version":3,"sources":["../../../../../backend/src/applications/users/guards/permissions.guard.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 { createMock, DeepMocked } from '@golevelup/ts-jest'\nimport { ExecutionContext, HttpException, Logger } from '@nestjs/common'\nimport { Reflector } from '@nestjs/core'\nimport { USER_PERMISSION, USER_ROLE } from '../constants/user'\nimport { UserHavePermission } from '../decorators/permissions.decorator'\nimport { UserModel } from '../models/user.model'\nimport { generateUserTest } from '../utils/test'\nimport { UserPermissionsGuard } from './permissions.guard'\n\ndescribe(UserPermissionsGuard.name, () => {\n let reflector: Reflector\n let permissionsGuard: UserPermissionsGuard\n let userTest: UserModel\n let context: DeepMocked<ExecutionContext>\n\n beforeAll(async () => {\n reflector = new Reflector()\n permissionsGuard = new UserPermissionsGuard(reflector)\n userTest = new UserModel(generateUserTest())\n Logger.overrideLogger(['fatal'])\n })\n\n it('should be defined', () => {\n expect(permissionsGuard).toBeDefined()\n expect(userTest).toBeDefined()\n })\n\n it('should pass with a valid permission', () => {\n userTest.applications = [USER_PERMISSION.PERSONAL_SPACE]\n context = createMock<ExecutionContext>()\n UserHavePermission(USER_PERMISSION.PERSONAL_SPACE)(context.getHandler())\n context.switchToHttp().getRequest.mockReturnValue({\n user: userTest\n })\n expect(permissionsGuard.canActivate(context)).toBe(true)\n })\n\n it('should pass if any of the permissions are granted', () => {\n userTest.applications = [USER_PERMISSION.PERSONAL_SPACE]\n context = createMock<ExecutionContext>()\n UserHavePermission([USER_PERMISSION.SPACES, USER_PERMISSION.PERSONAL_SPACE])(context.getHandler())\n context.switchToHttp().getRequest.mockReturnValue({\n user: userTest\n })\n expect(permissionsGuard.canActivate(context)).toBe(true)\n })\n\n it('should not pass with a bad permission', () => {\n userTest.applications = []\n context = createMock<ExecutionContext>()\n UserHavePermission(USER_PERMISSION.PERSONAL_SPACE)(context.getHandler())\n context.switchToHttp().getRequest.mockReturnValue({\n user: userTest\n })\n expect(() => permissionsGuard.canActivate(context)).toThrow(HttpException)\n })\n\n it('should pass with no permissions but with the admin role', () => {\n userTest.applications = []\n userTest.role = USER_ROLE.ADMINISTRATOR\n context = createMock<ExecutionContext>()\n UserHavePermission(USER_PERMISSION.PERSONAL_SPACE)(context.getHandler())\n context.switchToHttp().getRequest.mockReturnValue({\n user: userTest\n })\n expect(permissionsGuard.canActivate(context)).toBe(true)\n // reset\n userTest.role = USER_ROLE.USER\n })\n\n it('should not pass with a missing decorator', () => {\n userTest.applications = [USER_PERMISSION.PERSONAL_SPACE]\n context = createMock<ExecutionContext>()\n context.switchToHttp().getRequest.mockReturnValue({\n user: userTest\n })\n expect(permissionsGuard.canActivate(context)).toBeFalsy()\n })\n\n it('should pass with an empty decorator', () => {\n userTest.applications = [USER_PERMISSION.PERSONAL_SPACE]\n context = createMock<ExecutionContext>()\n UserHavePermission()(context.getHandler())\n context.switchToHttp().getRequest.mockReturnValue({\n user: userTest\n })\n expect(permissionsGuard.canActivate(context)).toBeTruthy()\n })\n})\n"],"names":["describe","UserPermissionsGuard","name","reflector","permissionsGuard","userTest","context","beforeAll","Reflector","UserModel","generateUserTest","Logger","overrideLogger","it","expect","toBeDefined","applications","USER_PERMISSION","PERSONAL_SPACE","createMock","UserHavePermission","getHandler","switchToHttp","getRequest","mockReturnValue","user","canActivate","toBe","SPACES","toThrow","HttpException","role","USER_ROLE","ADMINISTRATOR","USER","toBeFalsy","toBeTruthy"],"mappings":"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"}
|
|
@@ -35,7 +35,7 @@ let UserRolesGuard = class UserRolesGuard {
|
|
|
35
35
|
// used to bypass the check, the guard is called without argument, the value is '{}'
|
|
36
36
|
return true;
|
|
37
37
|
}
|
|
38
|
-
if (role === undefined) {
|
|
38
|
+
if (role === undefined || !Number.isFinite(role)) {
|
|
39
39
|
this.logger.warn(`no role defined on : ${ctx.getClass().name}:${ctx.getHandler().name}`);
|
|
40
40
|
return false;
|
|
41
41
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../backend/src/applications/users/guards/roles.guard.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 { CanActivate, ExecutionContext, Injectable, Logger } from '@nestjs/common'\nimport { Reflector } from '@nestjs/core'\nimport { FastifyAuthenticatedRequest } from '../../../authentication/interfaces/auth-request.interface'\nimport { USER_ROLE } from '../constants/user'\nimport { UserHaveRole } from '../decorators/roles.decorator'\n\n@Injectable()\nexport class UserRolesGuard implements CanActivate {\n private readonly logger = new Logger(UserRolesGuard.name)\n\n constructor(private readonly reflector: Reflector) {}\n\n canActivate(ctx: ExecutionContext): boolean {\n const role: USER_ROLE = this.reflector.getAllAndOverride(UserHaveRole, [ctx.getHandler(), ctx.getClass()])\n if (typeof role === 'object') {\n // used to bypass the check, the guard is called without argument, the value is '{}'\n return true\n }\n if (role === undefined) {\n this.logger.warn(`no role defined on : ${ctx.getClass().name}:${ctx.getHandler().name}`)\n return false\n }\n const req: FastifyAuthenticatedRequest = ctx.switchToHttp().getRequest()\n const authorized: boolean = req.user.haveRole(role)\n if (!authorized) {\n this.logger.warn(`does not have role : ${USER_ROLE[role]}`)\n }\n return authorized\n }\n}\n"],"names":["UserRolesGuard","canActivate","ctx","role","reflector","getAllAndOverride","UserHaveRole","getHandler","getClass","undefined","logger","warn","name","req","switchToHttp","getRequest","authorized","user","haveRole","USER_ROLE","Logger"],"mappings":"AAAA;;;;CAIC;;;;+BASYA;;;eAAAA;;;wBAPqD;sBACxC;sBAEA;gCACG;;;;;;;;;;AAGtB,IAAA,AAAMA,iBAAN,MAAMA;IAKXC,YAAYC,GAAqB,EAAW;QAC1C,MAAMC,OAAkB,IAAI,CAACC,SAAS,CAACC,iBAAiB,CAACC,4BAAY,EAAE;YAACJ,IAAIK,UAAU;YAAIL,IAAIM,QAAQ;SAAG;QACzG,IAAI,OAAOL,SAAS,UAAU;YAC5B,oFAAoF;YACpF,OAAO;QACT;QACA,IAAIA,SAASM,
|
|
1
|
+
{"version":3,"sources":["../../../../../backend/src/applications/users/guards/roles.guard.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 { CanActivate, ExecutionContext, Injectable, Logger } from '@nestjs/common'\nimport { Reflector } from '@nestjs/core'\nimport { FastifyAuthenticatedRequest } from '../../../authentication/interfaces/auth-request.interface'\nimport { USER_ROLE } from '../constants/user'\nimport { UserHaveRole } from '../decorators/roles.decorator'\n\n@Injectable()\nexport class UserRolesGuard implements CanActivate {\n private readonly logger = new Logger(UserRolesGuard.name)\n\n constructor(private readonly reflector: Reflector) {}\n\n canActivate(ctx: ExecutionContext): boolean {\n const role: USER_ROLE = this.reflector.getAllAndOverride(UserHaveRole, [ctx.getHandler(), ctx.getClass()])\n if (typeof role === 'object') {\n // used to bypass the check, the guard is called without argument, the value is '{}'\n return true\n }\n if (role === undefined || !Number.isFinite(role)) {\n this.logger.warn(`no role defined on : ${ctx.getClass().name}:${ctx.getHandler().name}`)\n return false\n }\n const req: FastifyAuthenticatedRequest = ctx.switchToHttp().getRequest()\n const authorized: boolean = req.user.haveRole(role)\n if (!authorized) {\n this.logger.warn(`does not have role : ${USER_ROLE[role]}`)\n }\n return authorized\n }\n}\n"],"names":["UserRolesGuard","canActivate","ctx","role","reflector","getAllAndOverride","UserHaveRole","getHandler","getClass","undefined","Number","isFinite","logger","warn","name","req","switchToHttp","getRequest","authorized","user","haveRole","USER_ROLE","Logger"],"mappings":"AAAA;;;;CAIC;;;;+BASYA;;;eAAAA;;;wBAPqD;sBACxC;sBAEA;gCACG;;;;;;;;;;AAGtB,IAAA,AAAMA,iBAAN,MAAMA;IAKXC,YAAYC,GAAqB,EAAW;QAC1C,MAAMC,OAAkB,IAAI,CAACC,SAAS,CAACC,iBAAiB,CAACC,4BAAY,EAAE;YAACJ,IAAIK,UAAU;YAAIL,IAAIM,QAAQ;SAAG;QACzG,IAAI,OAAOL,SAAS,UAAU;YAC5B,oFAAoF;YACpF,OAAO;QACT;QACA,IAAIA,SAASM,aAAa,CAACC,OAAOC,QAAQ,CAACR,OAAO;YAChD,IAAI,CAACS,MAAM,CAACC,IAAI,CAAC,CAAC,qBAAqB,EAAEX,IAAIM,QAAQ,GAAGM,IAAI,CAAC,CAAC,EAAEZ,IAAIK,UAAU,GAAGO,IAAI,EAAE;YACvF,OAAO;QACT;QACA,MAAMC,MAAmCb,IAAIc,YAAY,GAAGC,UAAU;QACtE,MAAMC,aAAsBH,IAAII,IAAI,CAACC,QAAQ,CAACjB;QAC9C,IAAI,CAACe,YAAY;YACf,IAAI,CAACN,MAAM,CAACC,IAAI,CAAC,CAAC,qBAAqB,EAAEQ,eAAS,CAAClB,KAAK,EAAE;QAC5D;QACA,OAAOe;IACT;IAlBA,YAAY,AAAiBd,SAAoB,CAAE;aAAtBA,YAAAA;aAFZQ,SAAS,IAAIU,cAAM,CAACtB,eAAec,IAAI;IAEJ;AAmBtD"}
|
|
@@ -170,7 +170,7 @@ let UserModel = class UserModel {
|
|
|
170
170
|
return UserModel.getFilesPath(userLogin);
|
|
171
171
|
}
|
|
172
172
|
constructor(props, removePassword = true){
|
|
173
|
-
// permissions as list
|
|
173
|
+
// permissions as a list
|
|
174
174
|
this.applications = [];
|
|
175
175
|
Object.assign(this, props);
|
|
176
176
|
if (removePassword) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../backend/src/applications/users/models/user.model.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 { Exclude, Expose } from 'class-transformer'\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport { configuration } from '../../../configuration/config.environment'\nimport { SPACE_REPOSITORY } from '../../spaces/constants/spaces'\nimport { GUEST_PERMISSION, USER_PATH, USER_PERMISSION, USER_PERMS_SEP, USER_ROLE } from '../constants/user'\nimport type { Owner } from '../interfaces/owner.interface'\nimport type { User } from '../schemas/user.interface'\n\nexport class UserModel implements User {\n id: number\n login: string\n email: string\n firstName: string\n lastName: string\n role: number\n language: string\n notification: number\n onlineStatus: number\n permissions: string\n storageUsage: number\n storageQuota: number\n isActive: boolean\n passwordAttempts: number\n currentIp: string\n lastIp: string\n currentAccess: Date\n lastAccess: Date\n createdAt: Date\n // exclusions\n @Exclude()\n password: string\n @Exclude()\n // only used on backend\n impersonatedFromId?: number\n impersonatedClientId?: string\n // used for desktop|cmd app\n clientId?: string\n\n // outside db schema\n fullName: string\n impersonated?: boolean\n avatarBase64?: string\n // permissions as list\n applications: string[] = []\n private _homePath: string\n private _filesPath: string\n private _trashPath: string\n private _tmpPath: string\n private _tasksPath: string\n\n @Exclude({ toPlainOnly: true })\n exp?: number // refresh token expiration needed to refresh\n\n constructor(props: Partial<User> & { exp?: number }, removePassword = true) {\n Object.assign(this, props)\n if (removePassword) {\n // always remove the password field from model for obvious security reasons\n // do not remove it from `props` to not mutate the object\n this.removePassword()\n }\n this.setFullName()\n this.setApplications()\n this.setProfile()\n }\n\n toString(): string {\n return `*User <${this.login}> (${this.id})*`\n }\n\n removePassword() {\n delete this.password\n }\n\n setFullName() {\n if (!this.fullName) {\n this.fullName = `${this.firstName || ''} ${this.lastName || ''}`.trim()\n }\n }\n\n @Expose()\n get isAdmin(): boolean {\n return this.role === USER_ROLE.ADMINISTRATOR\n }\n\n @Expose()\n get isUser(): boolean {\n return this.role === USER_ROLE.USER || this.role === USER_ROLE.ADMINISTRATOR\n }\n\n @Expose()\n get isGuest(): boolean {\n return this.role === USER_ROLE.GUEST\n }\n\n @Expose()\n get isLink(): boolean {\n return this.role === USER_ROLE.LINK\n }\n\n @Expose()\n get quotaIsExceeded(): boolean {\n return this.storageQuota !== null && this.storageUsage >= this.storageQuota\n }\n\n get homePath(): string {\n if (this.isLink || this.isGuest) {\n return (this._homePath ||= path.join(configuration.applications.files.tmpPath, this.isGuest ? 'guests' : 'links', this.login))\n }\n return (this._homePath ||= path.join(configuration.applications.files.usersPath, this.login))\n }\n\n get filesPath(): string {\n return (this._filesPath ||= path.join(this.homePath, SPACE_REPOSITORY.FILES))\n }\n\n get trashPath(): string {\n return (this._trashPath ||= path.join(this.homePath, SPACE_REPOSITORY.TRASH))\n }\n\n get tmpPath(): string {\n return (this._tmpPath ||= path.join(this.homePath, USER_PATH.TMP))\n }\n\n get tasksPath(): string {\n return (this._tasksPath ||= path.join(this.tmpPath, USER_PATH.TASKS))\n }\n\n async makePaths(): Promise<void> {\n if (this.isGuest || this.isLink) {\n await fs.mkdir(this.tasksPath, { recursive: true })\n } else {\n for (const p of [this.filesPath, this.trashPath, this.tasksPath]) {\n await fs.mkdir(p, { recursive: true })\n }\n }\n }\n\n asOwner(): Owner {\n return { id: this.id, login: this.login, email: this.email, fullName: this.fullName }\n }\n\n getInitials(): string {\n let initials: { f: string; l: string }\n if (this.firstName) {\n if (this.lastName) {\n initials = { f: this.firstName.charAt(0), l: this.lastName.charAt(0) }\n }\n initials = { f: this.firstName.charAt(0), l: this.firstName.charAt(1) }\n } else {\n initials = { f: this.login.charAt(0), l: this.login.charAt(1) }\n }\n return `${initials.f.toUpperCase()}${initials.l.toLowerCase()}`\n }\n\n private setProfile() {\n if (this.isLink) {\n this.login = `Link (${this.id})`\n this.email = 'guest-link@sync-in'\n }\n }\n\n private setApplications() {\n if (this.isGuest) {\n // dynamically set the permissions\n this.applications = Object.values(GUEST_PERMISSION)\n } else if (this.permissions) {\n this.applications = this.permissions.split(USER_PERMS_SEP)\n }\n delete this.permissions\n }\n\n havePermission(permission: string): boolean {\n if (this.isAdmin) {\n return true\n }\n if (permission === USER_PERMISSION.PERSONAL_SPACE && this.isGuest) {\n return false\n }\n return this.applications.indexOf(permission) !== -1\n }\n\n haveRole(role: number): boolean {\n return this.role <= role\n }\n\n static getHomePath(userLogin: string, isGuest = false, isLink = false): string {\n if (isGuest || isLink) {\n return path.join(configuration.applications.files.tmpPath, isGuest ? 'guests' : 'links', userLogin)\n }\n return path.join(configuration.applications.files.usersPath, userLogin)\n }\n\n static getFilesPath(userLogin: string): string {\n return path.join(UserModel.getHomePath(userLogin), SPACE_REPOSITORY.FILES)\n }\n\n static getTrashPath(userLogin: string): string {\n return path.join(UserModel.getHomePath(userLogin), SPACE_REPOSITORY.TRASH)\n }\n\n static getTasksPath(userLogin: string, isGuest = false, isLink = false): string {\n return path.join(UserModel.getHomePath(userLogin, isGuest, isLink), USER_PATH.TMP, USER_PATH.TASKS)\n }\n\n static getRepositoryPath(userLogin: string, inTrash = false): string {\n if (inTrash) return UserModel.getTrashPath(userLogin)\n return UserModel.getFilesPath(userLogin)\n }\n}\n"],"names":["UserModel","toString","login","id","removePassword","password","setFullName","fullName","firstName","lastName","trim","isAdmin","role","USER_ROLE","ADMINISTRATOR","isUser","USER","isGuest","GUEST","isLink","LINK","quotaIsExceeded","storageQuota","storageUsage","homePath","_homePath","path","join","configuration","applications","files","tmpPath","usersPath","filesPath","_filesPath","SPACE_REPOSITORY","FILES","trashPath","_trashPath","TRASH","_tmpPath","USER_PATH","TMP","tasksPath","_tasksPath","TASKS","makePaths","fs","mkdir","recursive","p","asOwner","email","getInitials","initials","f","charAt","l","toUpperCase","toLowerCase","setProfile","setApplications","Object","values","GUEST_PERMISSION","permissions","split","USER_PERMS_SEP","havePermission","permission","USER_PERMISSION","PERSONAL_SPACE","indexOf","haveRole","getHomePath","userLogin","getFilesPath","getTrashPath","getTasksPath","getRepositoryPath","inTrash","props","assign","toPlainOnly"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../backend/src/applications/users/models/user.model.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 { Exclude, Expose } from 'class-transformer'\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport { configuration } from '../../../configuration/config.environment'\nimport { SPACE_REPOSITORY } from '../../spaces/constants/spaces'\nimport { GUEST_PERMISSION, USER_PATH, USER_PERMISSION, USER_PERMS_SEP, USER_ROLE } from '../constants/user'\nimport type { Owner } from '../interfaces/owner.interface'\nimport type { User } from '../schemas/user.interface'\n\nexport class UserModel implements User {\n id: number\n login: string\n email: string\n firstName: string\n lastName: string\n role: number\n language: string\n notification: number\n onlineStatus: number\n permissions: string\n storageUsage: number\n storageQuota: number\n isActive: boolean\n passwordAttempts: number\n currentIp: string\n lastIp: string\n currentAccess: Date\n lastAccess: Date\n createdAt: Date\n // exclusions\n @Exclude()\n password: string\n @Exclude()\n // only used on backend\n impersonatedFromId?: number\n impersonatedClientId?: string\n // used for desktop|cmd app\n clientId?: string\n\n // outside db schema\n fullName: string\n impersonated?: boolean\n avatarBase64?: string\n // permissions as a list\n applications: string[] = []\n private _homePath: string\n private _filesPath: string\n private _trashPath: string\n private _tmpPath: string\n private _tasksPath: string\n\n @Exclude({ toPlainOnly: true })\n exp?: number // refresh token expiration needed to refresh\n\n constructor(props: Partial<User> & { exp?: number }, removePassword = true) {\n Object.assign(this, props)\n if (removePassword) {\n // always remove the password field from model for obvious security reasons\n // do not remove it from `props` to not mutate the object\n this.removePassword()\n }\n this.setFullName()\n this.setApplications()\n this.setProfile()\n }\n\n toString(): string {\n return `*User <${this.login}> (${this.id})*`\n }\n\n removePassword() {\n delete this.password\n }\n\n setFullName() {\n if (!this.fullName) {\n this.fullName = `${this.firstName || ''} ${this.lastName || ''}`.trim()\n }\n }\n\n @Expose()\n get isAdmin(): boolean {\n return this.role === USER_ROLE.ADMINISTRATOR\n }\n\n @Expose()\n get isUser(): boolean {\n return this.role === USER_ROLE.USER || this.role === USER_ROLE.ADMINISTRATOR\n }\n\n @Expose()\n get isGuest(): boolean {\n return this.role === USER_ROLE.GUEST\n }\n\n @Expose()\n get isLink(): boolean {\n return this.role === USER_ROLE.LINK\n }\n\n @Expose()\n get quotaIsExceeded(): boolean {\n return this.storageQuota !== null && this.storageUsage >= this.storageQuota\n }\n\n get homePath(): string {\n if (this.isLink || this.isGuest) {\n return (this._homePath ||= path.join(configuration.applications.files.tmpPath, this.isGuest ? 'guests' : 'links', this.login))\n }\n return (this._homePath ||= path.join(configuration.applications.files.usersPath, this.login))\n }\n\n get filesPath(): string {\n return (this._filesPath ||= path.join(this.homePath, SPACE_REPOSITORY.FILES))\n }\n\n get trashPath(): string {\n return (this._trashPath ||= path.join(this.homePath, SPACE_REPOSITORY.TRASH))\n }\n\n get tmpPath(): string {\n return (this._tmpPath ||= path.join(this.homePath, USER_PATH.TMP))\n }\n\n get tasksPath(): string {\n return (this._tasksPath ||= path.join(this.tmpPath, USER_PATH.TASKS))\n }\n\n async makePaths(): Promise<void> {\n if (this.isGuest || this.isLink) {\n await fs.mkdir(this.tasksPath, { recursive: true })\n } else {\n for (const p of [this.filesPath, this.trashPath, this.tasksPath]) {\n await fs.mkdir(p, { recursive: true })\n }\n }\n }\n\n asOwner(): Owner {\n return { id: this.id, login: this.login, email: this.email, fullName: this.fullName }\n }\n\n getInitials(): string {\n let initials: { f: string; l: string }\n if (this.firstName) {\n if (this.lastName) {\n initials = { f: this.firstName.charAt(0), l: this.lastName.charAt(0) }\n }\n initials = { f: this.firstName.charAt(0), l: this.firstName.charAt(1) }\n } else {\n initials = { f: this.login.charAt(0), l: this.login.charAt(1) }\n }\n return `${initials.f.toUpperCase()}${initials.l.toLowerCase()}`\n }\n\n private setProfile() {\n if (this.isLink) {\n this.login = `Link (${this.id})`\n this.email = 'guest-link@sync-in'\n }\n }\n\n private setApplications() {\n if (this.isGuest) {\n // dynamically set the permissions\n this.applications = Object.values(GUEST_PERMISSION)\n } else if (this.permissions) {\n this.applications = this.permissions.split(USER_PERMS_SEP)\n }\n delete this.permissions\n }\n\n havePermission(permission: string): boolean {\n if (this.isAdmin) {\n return true\n }\n if (permission === USER_PERMISSION.PERSONAL_SPACE && this.isGuest) {\n return false\n }\n return this.applications.indexOf(permission) !== -1\n }\n\n haveRole(role: number): boolean {\n return this.role <= role\n }\n\n static getHomePath(userLogin: string, isGuest = false, isLink = false): string {\n if (isGuest || isLink) {\n return path.join(configuration.applications.files.tmpPath, isGuest ? 'guests' : 'links', userLogin)\n }\n return path.join(configuration.applications.files.usersPath, userLogin)\n }\n\n static getFilesPath(userLogin: string): string {\n return path.join(UserModel.getHomePath(userLogin), SPACE_REPOSITORY.FILES)\n }\n\n static getTrashPath(userLogin: string): string {\n return path.join(UserModel.getHomePath(userLogin), SPACE_REPOSITORY.TRASH)\n }\n\n static getTasksPath(userLogin: string, isGuest = false, isLink = false): string {\n return path.join(UserModel.getHomePath(userLogin, isGuest, isLink), USER_PATH.TMP, USER_PATH.TASKS)\n }\n\n static getRepositoryPath(userLogin: string, inTrash = false): string {\n if (inTrash) return UserModel.getTrashPath(userLogin)\n return UserModel.getFilesPath(userLogin)\n }\n}\n"],"names":["UserModel","toString","login","id","removePassword","password","setFullName","fullName","firstName","lastName","trim","isAdmin","role","USER_ROLE","ADMINISTRATOR","isUser","USER","isGuest","GUEST","isLink","LINK","quotaIsExceeded","storageQuota","storageUsage","homePath","_homePath","path","join","configuration","applications","files","tmpPath","usersPath","filesPath","_filesPath","SPACE_REPOSITORY","FILES","trashPath","_trashPath","TRASH","_tmpPath","USER_PATH","TMP","tasksPath","_tasksPath","TASKS","makePaths","fs","mkdir","recursive","p","asOwner","email","getInitials","initials","f","charAt","l","toUpperCase","toLowerCase","setProfile","setApplications","Object","values","GUEST_PERMISSION","permissions","split","USER_PERMS_SEP","havePermission","permission","USER_PERMISSION","PERSONAL_SPACE","indexOf","haveRole","getHomePath","userLogin","getFilesPath","getTrashPath","getTasksPath","getRepositoryPath","inTrash","props","assign","toPlainOnly"],"mappings":"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"}
|
|
@@ -221,7 +221,7 @@ let AdminUsersManager = class AdminUsersManager {
|
|
|
221
221
|
};
|
|
222
222
|
}
|
|
223
223
|
async getGroup(groupId) {
|
|
224
|
-
const group = this.adminQueries.groupFromId(groupId);
|
|
224
|
+
const group = await this.adminQueries.groupFromId(groupId);
|
|
225
225
|
if (!group) {
|
|
226
226
|
throw new _common.HttpException('Group not found', _common.HttpStatus.NOT_FOUND);
|
|
227
227
|
}
|
|
@@ -325,29 +325,6 @@ let AdminUsersManager = class AdminUsersManager {
|
|
|
325
325
|
throw new _common.HttpException('Unable to delete user space', _common.HttpStatus.INTERNAL_SERVER_ERROR);
|
|
326
326
|
}
|
|
327
327
|
}
|
|
328
|
-
async renameUserSpace(oldLogin, newLogin) {
|
|
329
|
-
const currentUserSpace = _usermodel.UserModel.getHomePath(oldLogin);
|
|
330
|
-
if (await (0, _files.isPathExists)(currentUserSpace)) {
|
|
331
|
-
const newUserSpace = _usermodel.UserModel.getHomePath(newLogin);
|
|
332
|
-
if (await (0, _files.isPathExists)(newUserSpace)) {
|
|
333
|
-
this.logger.warn(`${this.renameUserSpace.name} - user space *${newLogin}* already exists : ${newUserSpace}`);
|
|
334
|
-
return false;
|
|
335
|
-
} else {
|
|
336
|
-
try {
|
|
337
|
-
await (0, _files.moveFiles)(currentUserSpace, newUserSpace);
|
|
338
|
-
return true;
|
|
339
|
-
} catch (e) {
|
|
340
|
-
// try to restore
|
|
341
|
-
await (0, _files.moveFiles)(newUserSpace, currentUserSpace, true);
|
|
342
|
-
this.logger.error(`${this.renameUserSpace.name} - unable to rename user space from *${currentUserSpace}* to *${newUserSpace}* : ${e}`);
|
|
343
|
-
return false;
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
} else {
|
|
347
|
-
this.logger.warn(`${this.renameUserSpace.name} - user space *${oldLogin}* does not exist : ${currentUserSpace}`);
|
|
348
|
-
return false;
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
328
|
checkUser(user, checkOnly = false) {
|
|
352
329
|
if (!user) {
|
|
353
330
|
throw new _common.HttpException('User not found', _common.HttpStatus.NOT_FOUND);
|
|
@@ -356,6 +333,27 @@ let AdminUsersManager = class AdminUsersManager {
|
|
|
356
333
|
return new _usermodel.UserModel(user, true);
|
|
357
334
|
}
|
|
358
335
|
}
|
|
336
|
+
async renameUserSpace(oldLogin, newLogin) {
|
|
337
|
+
const currentUserSpace = _usermodel.UserModel.getHomePath(oldLogin);
|
|
338
|
+
if (!await (0, _files.isPathExists)(currentUserSpace)) {
|
|
339
|
+
this.logger.warn(`${this.renameUserSpace.name} - user space *${oldLogin}* does not exist : ${currentUserSpace}`);
|
|
340
|
+
return false;
|
|
341
|
+
}
|
|
342
|
+
const newUserSpace = _usermodel.UserModel.getHomePath(newLogin);
|
|
343
|
+
if (await (0, _files.isPathExists)(newUserSpace)) {
|
|
344
|
+
this.logger.warn(`${this.renameUserSpace.name} - user space *${newLogin}* already exists : ${newUserSpace}`);
|
|
345
|
+
return false;
|
|
346
|
+
}
|
|
347
|
+
try {
|
|
348
|
+
await (0, _files.moveFiles)(currentUserSpace, newUserSpace);
|
|
349
|
+
return true;
|
|
350
|
+
} catch (e) {
|
|
351
|
+
// try to restore
|
|
352
|
+
await (0, _files.moveFiles)(newUserSpace, currentUserSpace, true);
|
|
353
|
+
this.logger.error(`${this.renameUserSpace.name} - unable to rename user space from *${currentUserSpace}* to *${newUserSpace}* : ${e}`);
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
359
357
|
async checkGroupNameExists(groupName) {
|
|
360
358
|
if (await this.adminQueries.usersQueries.checkGroupNameExists(groupName)) {
|
|
361
359
|
throw new _common.HttpException('Name already used', _common.HttpStatus.BAD_REQUEST);
|