@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
|
@@ -7,13 +7,14 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
7
7
|
value: true
|
|
8
8
|
});
|
|
9
9
|
const _testing = require("@nestjs/testing");
|
|
10
|
+
const _ldapts = require("ldapts");
|
|
10
11
|
const _appconstants = require("../../../app.constants");
|
|
11
12
|
const _adminusersmanagerservice = require("../../../applications/users/services/admin-users-manager.service");
|
|
12
13
|
const _usersmanagerservice = require("../../../applications/users/services/users-manager.service");
|
|
13
|
-
const _authmethodldapservice = require("./auth-method-ldap.service");
|
|
14
|
-
const _configenvironment = require("../../../configuration/config.environment");
|
|
15
|
-
const _ldapts = require("ldapts");
|
|
16
14
|
const _functions = /*#__PURE__*/ _interop_require_wildcard(require("../../../common/functions"));
|
|
15
|
+
const _configenvironment = require("../../../configuration/config.environment");
|
|
16
|
+
const _authldap = require("../../constants/auth-ldap");
|
|
17
|
+
const _authmethodldapservice = require("./auth-method-ldap.service");
|
|
17
18
|
function _getRequireWildcardCache(nodeInterop) {
|
|
18
19
|
if (typeof WeakMap !== "function") return null;
|
|
19
20
|
var cacheBabelInterop = new WeakMap();
|
|
@@ -57,17 +58,17 @@ function _interop_require_wildcard(obj, nodeInterop) {
|
|
|
57
58
|
}
|
|
58
59
|
// Mock ldapts Client to simulate LDAP behaviors
|
|
59
60
|
jest.mock('ldapts', ()=>{
|
|
60
|
-
|
|
61
|
-
};
|
|
61
|
+
const actual = jest.requireActual('ldapts');
|
|
62
62
|
const mockClientInstance = {
|
|
63
63
|
bind: jest.fn(),
|
|
64
64
|
search: jest.fn(),
|
|
65
65
|
unbind: jest.fn()
|
|
66
66
|
};
|
|
67
67
|
const Client = jest.fn().mockImplementation(()=>mockClientInstance);
|
|
68
|
+
// Conserver tous les autres exports réels (dont EqualityFilter, AndFilter, InvalidCredentialsError, etc.)
|
|
68
69
|
return {
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
...actual,
|
|
71
|
+
Client
|
|
71
72
|
};
|
|
72
73
|
});
|
|
73
74
|
// --- Test helpers (DRY) ---
|
|
@@ -97,6 +98,7 @@ const buildUser = (overrides = {})=>({
|
|
|
97
98
|
isGuest: false,
|
|
98
99
|
isActive: true,
|
|
99
100
|
makePaths: jest.fn().mockResolvedValue(undefined),
|
|
101
|
+
setFullName: jest.fn(),
|
|
100
102
|
...overrides
|
|
101
103
|
});
|
|
102
104
|
// --------------------------
|
|
@@ -117,6 +119,17 @@ describe(_authmethodldapservice.AuthMethodLdapService.name, ()=>{
|
|
|
117
119
|
};
|
|
118
120
|
const spyLoggerError = ()=>jest.spyOn(authMethodLdapService['logger'], 'error').mockImplementation(()=>undefined);
|
|
119
121
|
beforeAll(async ()=>{
|
|
122
|
+
_configenvironment.configuration.auth.ldap = {
|
|
123
|
+
servers: [
|
|
124
|
+
'ldap://localhost:389'
|
|
125
|
+
],
|
|
126
|
+
attributes: {
|
|
127
|
+
login: _authldap.LDAP_LOGIN_ATTR.UID,
|
|
128
|
+
email: 'mail'
|
|
129
|
+
},
|
|
130
|
+
baseDN: 'ou=people,dc=example,dc=org',
|
|
131
|
+
filter: ''
|
|
132
|
+
};
|
|
120
133
|
const module = await _testing.Test.createTestingModule({
|
|
121
134
|
providers: [
|
|
122
135
|
_authmethodldapservice.AuthMethodLdapService,
|
|
@@ -125,7 +138,9 @@ describe(_authmethodldapservice.AuthMethodLdapService.name, ()=>{
|
|
|
125
138
|
useValue: {
|
|
126
139
|
findUser: jest.fn(),
|
|
127
140
|
logUser: jest.fn(),
|
|
128
|
-
updateAccesses: jest.fn().mockResolvedValue(undefined)
|
|
141
|
+
updateAccesses: jest.fn().mockResolvedValue(undefined),
|
|
142
|
+
validateAppPassword: jest.fn(),
|
|
143
|
+
fromUserId: jest.fn()
|
|
129
144
|
}
|
|
130
145
|
},
|
|
131
146
|
{
|
|
@@ -143,14 +158,6 @@ describe(_authmethodldapservice.AuthMethodLdapService.name, ()=>{
|
|
|
143
158
|
authMethodLdapService = module.get(_authmethodldapservice.AuthMethodLdapService);
|
|
144
159
|
adminUsersManager = module.get(_adminusersmanagerservice.AdminUsersManager);
|
|
145
160
|
usersManager = module.get(_usersmanagerservice.UsersManager);
|
|
146
|
-
_configenvironment.configuration.auth.ldap = {
|
|
147
|
-
servers: [
|
|
148
|
-
'ldap://localhost:389'
|
|
149
|
-
],
|
|
150
|
-
loginAttribute: 'uid',
|
|
151
|
-
baseDN: 'ou=people,dc=example,dc=org',
|
|
152
|
-
filter: ''
|
|
153
|
-
};
|
|
154
161
|
});
|
|
155
162
|
it('should be defined', ()=>{
|
|
156
163
|
expect(authMethodLdapService).toBeDefined();
|
|
@@ -177,7 +184,7 @@ describe(_authmethodldapservice.AuthMethodLdapService.name, ()=>{
|
|
|
177
184
|
expect(usersManager.logUser).toHaveBeenCalledWith(guestUser, 'pass', '127.0.0.1');
|
|
178
185
|
expect(_ldapts.Client).not.toHaveBeenCalled(); // client should not be constructed
|
|
179
186
|
});
|
|
180
|
-
it('should throw FORBIDDEN for locked account and LDAP login mismatch', async ()=>{
|
|
187
|
+
it('should throw FORBIDDEN for locked account and resolve null for LDAP login mismatch', async ()=>{
|
|
181
188
|
// Phase 1: locked account
|
|
182
189
|
usersManager.findUser.mockResolvedValue({
|
|
183
190
|
login: 'john',
|
|
@@ -187,13 +194,11 @@ describe(_authmethodldapservice.AuthMethodLdapService.name, ()=>{
|
|
|
187
194
|
const loggerErrorSpy1 = jest.spyOn(authMethodLdapService['logger'], 'error').mockImplementation(()=>undefined);
|
|
188
195
|
await expect(authMethodLdapService.validateUser('john', 'pwd')).rejects.toThrow(/account locked/i);
|
|
189
196
|
expect(loggerErrorSpy1).toHaveBeenCalled();
|
|
190
|
-
// Phase 2: mismatch between requested login and LDAP returned login
|
|
197
|
+
// Phase 2: mismatch between requested login and LDAP returned login -> service renvoie null
|
|
191
198
|
const existingUser = buildUser({
|
|
192
199
|
id: 8
|
|
193
200
|
});
|
|
194
201
|
usersManager.findUser.mockResolvedValue(existingUser);
|
|
195
|
-
const originalAttr = _configenvironment.configuration.auth.ldap.loginAttribute;
|
|
196
|
-
_configenvironment.configuration.auth.ldap.loginAttribute = 'cn';
|
|
197
202
|
mockBindResolve(ldapClient);
|
|
198
203
|
mockSearchEntries(ldapClient, [
|
|
199
204
|
{
|
|
@@ -203,8 +208,6 @@ describe(_authmethodldapservice.AuthMethodLdapService.name, ()=>{
|
|
|
203
208
|
}
|
|
204
209
|
]);
|
|
205
210
|
await expect(authMethodLdapService.validateUser('john', 'pwd')).rejects.toThrow(/account matching error/i);
|
|
206
|
-
// restore the attribute to avoid impacting other tests
|
|
207
|
-
_configenvironment.configuration.auth.ldap.loginAttribute = originalAttr;
|
|
208
211
|
});
|
|
209
212
|
it('should handle invalid LDAP credentials for both existing and unknown users', async ()=>{
|
|
210
213
|
// Phase 1: existing user -> updateAccesses invoked with success=false and logger.error intercepted
|
|
@@ -248,14 +251,15 @@ describe(_authmethodldapservice.AuthMethodLdapService.name, ()=>{
|
|
|
248
251
|
expect(adminUsersManager.createUserOrGuest).not.toHaveBeenCalled();
|
|
249
252
|
expect(loggerErrorSpy).toHaveBeenCalled();
|
|
250
253
|
// Phase 2: create a new user (success, single email)
|
|
254
|
+
// Stub directement checkAuth pour retourner une entrée LDAP valide
|
|
255
|
+
const checkAuthSpy = jest.spyOn(authMethodLdapService, 'checkAuth');
|
|
256
|
+
checkAuthSpy.mockResolvedValueOnce({
|
|
257
|
+
uid: 'john',
|
|
258
|
+
cn: 'John Doe',
|
|
259
|
+
mail: 'john@example.org'
|
|
260
|
+
});
|
|
261
|
+
adminUsersManager.createUserOrGuest.mockClear();
|
|
251
262
|
usersManager.findUser.mockResolvedValue(null);
|
|
252
|
-
setupLdapSuccess([
|
|
253
|
-
{
|
|
254
|
-
uid: 'john',
|
|
255
|
-
cn: 'John Doe',
|
|
256
|
-
mail: 'john@example.org'
|
|
257
|
-
}
|
|
258
|
-
]);
|
|
259
263
|
const createdUser = {
|
|
260
264
|
id: 2,
|
|
261
265
|
login: 'john',
|
|
@@ -264,6 +268,8 @@ describe(_authmethodldapservice.AuthMethodLdapService.name, ()=>{
|
|
|
264
268
|
makePaths: jest.fn()
|
|
265
269
|
};
|
|
266
270
|
adminUsersManager.createUserOrGuest.mockResolvedValue(createdUser);
|
|
271
|
+
// If the service reloads the user via fromUserId after creation
|
|
272
|
+
usersManager.fromUserId.mockResolvedValue(createdUser);
|
|
267
273
|
// Cover the success-flow catch branch
|
|
268
274
|
const loggerErrorSpy2 = spyLoggerError();
|
|
269
275
|
usersManager.updateAccesses.mockRejectedValueOnce(new Error('updateAccesses success flow boom'));
|
|
@@ -280,6 +286,7 @@ describe(_authmethodldapservice.AuthMethodLdapService.name, ()=>{
|
|
|
280
286
|
expect(usersManager.updateAccesses).toHaveBeenCalledWith(createdUser, '192.168.1.10', true);
|
|
281
287
|
expect(loggerErrorSpy2).toHaveBeenCalled();
|
|
282
288
|
// Phase 3: multiple emails -> keep the first
|
|
289
|
+
adminUsersManager.createUserOrGuest.mockClear();
|
|
283
290
|
usersManager.findUser.mockResolvedValue(null);
|
|
284
291
|
setupLdapSuccess([
|
|
285
292
|
{
|
|
@@ -297,6 +304,7 @@ describe(_authmethodldapservice.AuthMethodLdapService.name, ()=>{
|
|
|
297
304
|
makePaths: jest.fn()
|
|
298
305
|
};
|
|
299
306
|
adminUsersManager.createUserOrGuest.mockResolvedValue(createdUser2);
|
|
307
|
+
usersManager.fromUserId.mockResolvedValue(createdUser2);
|
|
300
308
|
const resC = await authMethodLdapService.validateUser('multi', 'pwd');
|
|
301
309
|
expect(adminUsersManager.createUserOrGuest).toHaveBeenCalledWith(expect.objectContaining({
|
|
302
310
|
email: 'first@example.org'
|
|
@@ -404,6 +412,20 @@ describe(_authmethodldapservice.AuthMethodLdapService.name, ()=>{
|
|
|
404
412
|
expect(resB).toBeNull();
|
|
405
413
|
expect(usersManager.updateAccesses).toHaveBeenCalledWith(existingUser2, '1.1.1.1', false);
|
|
406
414
|
});
|
|
415
|
+
it('should allow app password when LDAP fails and scope is provided', async ()=>{
|
|
416
|
+
const existingUser = buildUser({
|
|
417
|
+
id: 42
|
|
418
|
+
});
|
|
419
|
+
usersManager.findUser.mockResolvedValue(existingUser);
|
|
420
|
+
// LDAP invalid credentials
|
|
421
|
+
mockBindRejectInvalid(ldapClient, _ldapts.InvalidCredentialsError, 'invalid credentials');
|
|
422
|
+
// App password success
|
|
423
|
+
usersManager.validateAppPassword.mockResolvedValue(true);
|
|
424
|
+
const res = await authMethodLdapService.validateUser('john', 'app-password', '10.0.0.2', 'webdav');
|
|
425
|
+
expect(res).toBe(existingUser);
|
|
426
|
+
expect(usersManager.validateAppPassword).toHaveBeenCalledWith(existingUser, 'app-password', '10.0.0.2', 'webdav');
|
|
427
|
+
expect(usersManager.updateAccesses).toHaveBeenCalledWith(existingUser, '10.0.0.2', true);
|
|
428
|
+
});
|
|
407
429
|
it('should throw 500 when LDAP connection error occurs during bind', async ()=>{
|
|
408
430
|
// Arrange: no existing user to reach checkAuth flow
|
|
409
431
|
usersManager.findUser.mockResolvedValue(null);
|
|
@@ -431,7 +453,7 @@ describe(_authmethodldapservice.AuthMethodLdapService.name, ()=>{
|
|
|
431
453
|
expect(res).toBeNull();
|
|
432
454
|
expect(usersManager.updateAccesses).not.toHaveBeenCalled();
|
|
433
455
|
});
|
|
434
|
-
it('should log update failure
|
|
456
|
+
it('should log update failure when updating existing user', async ()=>{
|
|
435
457
|
// Arrange: existing user with changed identity
|
|
436
458
|
const existingUser = buildUser({
|
|
437
459
|
id: 11,
|
|
@@ -439,7 +461,6 @@ describe(_authmethodldapservice.AuthMethodLdapService.name, ()=>{
|
|
|
439
461
|
});
|
|
440
462
|
usersManager.findUser.mockResolvedValue(existingUser);
|
|
441
463
|
// Ensure LDAP loginAttribute matches uid for this test (a previous test sets it to 'cn')
|
|
442
|
-
_configenvironment.configuration.auth.ldap.loginAttribute = 'uid';
|
|
443
464
|
setupLdapSuccess([
|
|
444
465
|
{
|
|
445
466
|
uid: 'john',
|
|
@@ -456,8 +477,6 @@ describe(_authmethodldapservice.AuthMethodLdapService.name, ()=>{
|
|
|
456
477
|
});
|
|
457
478
|
const res = await authMethodLdapService.validateUser('john', 'pwd');
|
|
458
479
|
expect(adminUsersManager.updateUserOrGuest).toHaveBeenCalled();
|
|
459
|
-
// makePaths still invoked
|
|
460
|
-
expect(existingUser.makePaths).toHaveBeenCalled();
|
|
461
480
|
// Local fields unchanged since update failed
|
|
462
481
|
expect(existingUser.email).toBe('old@ex.org');
|
|
463
482
|
expect(res).toBe(existingUser);
|
|
@@ -471,22 +490,7 @@ describe(_authmethodldapservice.AuthMethodLdapService.name, ()=>{
|
|
|
471
490
|
isActive: true
|
|
472
491
|
};
|
|
473
492
|
usersManager.findUser.mockResolvedValue(userA);
|
|
474
|
-
_configenvironment.configuration.auth.ldap.loginAttribute = 'uid';
|
|
475
493
|
ldapClient.bind.mockResolvedValue(undefined);
|
|
476
|
-
// Non-matching entry: uid !== requested uid
|
|
477
|
-
ldapClient.search.mockResolvedValue({
|
|
478
|
-
searchEntries: [
|
|
479
|
-
{
|
|
480
|
-
uid: 'jane',
|
|
481
|
-
cn: 'Jane Doe',
|
|
482
|
-
mail: 'jane@example.org'
|
|
483
|
-
}
|
|
484
|
-
]
|
|
485
|
-
});
|
|
486
|
-
ldapClient.unbind.mockResolvedValue(undefined);
|
|
487
|
-
const resA = await authMethodLdapService.validateUser('john', 'pwd', '3.3.3.3');
|
|
488
|
-
expect(resA).toBeNull();
|
|
489
|
-
expect(usersManager.updateAccesses).toHaveBeenCalledWith(userA, '3.3.3.3', false);
|
|
490
494
|
// Phase B: Matching entry + password considered changed -> updateUserOrGuest called, password not reassigned locally
|
|
491
495
|
jest.clearAllMocks();
|
|
492
496
|
const userB = buildUser({
|
|
@@ -494,7 +498,6 @@ describe(_authmethodldapservice.AuthMethodLdapService.name, ()=>{
|
|
|
494
498
|
email: 'old@ex.org'
|
|
495
499
|
});
|
|
496
500
|
usersManager.findUser.mockResolvedValue(userB);
|
|
497
|
-
_configenvironment.configuration.auth.ldap.loginAttribute = 'uid';
|
|
498
501
|
setupLdapSuccess([
|
|
499
502
|
{
|
|
500
503
|
uid: 'john',
|
|
@@ -523,7 +526,6 @@ describe(_authmethodldapservice.AuthMethodLdapService.name, ()=>{
|
|
|
523
526
|
firstName: 'John',
|
|
524
527
|
lastName: 'Doe'
|
|
525
528
|
});
|
|
526
|
-
expect(userB.makePaths).toHaveBeenCalled();
|
|
527
529
|
expect(resB).toBe(userB);
|
|
528
530
|
});
|
|
529
531
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../backend/src/authentication/services/auth-methods/auth-method-ldap.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 { CONNECT_ERROR_CODE } from '../../../app.constants'\nimport { UserModel } from '../../../applications/users/models/user.model'\nimport { AdminUsersManager } from '../../../applications/users/services/admin-users-manager.service'\nimport { UsersManager } from '../../../applications/users/services/users-manager.service'\nimport { AuthMethodLdapService } from './auth-method-ldap.service'\nimport { configuration } from '../../../configuration/config.environment'\nimport { Client, InvalidCredentialsError } from 'ldapts'\nimport { Mocked } from 'jest-mock'\nimport * as commonFunctions from '../../../common/functions'\n\n// Mock ldapts Client to simulate LDAP behaviors\njest.mock('ldapts', () => {\n class InvalidCredentialsError extends Error {}\n const mockClientInstance = {\n bind: jest.fn(),\n search: jest.fn(),\n unbind: jest.fn()\n }\n const Client = jest.fn().mockImplementation(() => mockClientInstance)\n return { Client, InvalidCredentialsError }\n})\n\n// --- Test helpers (DRY) ---\n// Reusable LDAP mocks\nconst mockBindResolve = (ldapClient: any) => {\n ldapClient.bind.mockResolvedValue(undefined)\n ldapClient.unbind.mockResolvedValue(undefined)\n}\nconst mockBindRejectInvalid = (ldapClient: any, InvalidCredentialsErrorCtor: any, message = 'invalid') => {\n ldapClient.bind.mockRejectedValue(new InvalidCredentialsErrorCtor(message))\n ldapClient.unbind.mockResolvedValue(undefined)\n}\nconst mockSearchEntries = (ldapClient: any, entries: any[]) => {\n ldapClient.search.mockResolvedValue({ searchEntries: entries })\n}\nconst mockSearchReject = (ldapClient: any, err: Error) => {\n ldapClient.search.mockRejectedValue(err)\n}\n// User factory\nconst buildUser = (overrides: Partial<UserModel> = {}) =>\n ({\n id: 0,\n login: 'john',\n email: 'old@example.org',\n password: 'hashed',\n isGuest: false,\n isActive: true,\n makePaths: jest.fn().mockResolvedValue(undefined),\n ...overrides\n }) as any\n\n// --------------------------\n\ndescribe(AuthMethodLdapService.name, () => {\n let authMethodLdapService: AuthMethodLdapService\n let usersManager: Mocked<UsersManager>\n let adminUsersManager: Mocked<AdminUsersManager>\n const ldapClient = {\n bind: jest.fn(),\n search: jest.fn(),\n unbind: jest.fn()\n }\n ;(Client as Mocked<any>).mockImplementation(() => ldapClient)\n\n // Local helpers (need access to authMethodLdapService and ldapClient in this scope)\n const setupLdapSuccess = (entries: any[]) => {\n mockBindResolve(ldapClient)\n mockSearchEntries(ldapClient, entries)\n }\n const spyLoggerError = () => jest.spyOn(authMethodLdapService['logger'], 'error').mockImplementation(() => undefined as any)\n\n beforeAll(async () => {\n const module: TestingModule = await Test.createTestingModule({\n providers: [\n AuthMethodLdapService,\n {\n provide: UsersManager,\n useValue: {\n findUser: jest.fn(),\n logUser: jest.fn(),\n updateAccesses: jest.fn().mockResolvedValue(undefined)\n }\n },\n {\n provide: AdminUsersManager,\n useValue: {\n createUserOrGuest: jest.fn(),\n updateUserOrGuest: jest.fn()\n }\n }\n ]\n }).compile()\n\n module.useLogger(['fatal'])\n authMethodLdapService = module.get<AuthMethodLdapService>(AuthMethodLdapService)\n adminUsersManager = module.get<Mocked<AdminUsersManager>>(AdminUsersManager)\n usersManager = module.get<Mocked<UsersManager>>(UsersManager)\n configuration.auth.ldap = { servers: ['ldap://localhost:389'], loginAttribute: 'uid', baseDN: 'ou=people,dc=example,dc=org', filter: '' }\n })\n\n it('should be defined', () => {\n expect(authMethodLdapService).toBeDefined()\n expect(usersManager).toBeDefined()\n expect(adminUsersManager).toBeDefined()\n expect(ldapClient).toBeDefined()\n })\n\n it('should authenticate a guest user via database and bypass LDAP', async () => {\n // Arrange\n const guestUser: any = { id: 1, login: 'guest1', isGuest: true, isActive: true }\n usersManager.findUser.mockResolvedValue(guestUser)\n const dbAuthResult: any = { ...guestUser, token: 'jwt' }\n usersManager.logUser.mockResolvedValue(dbAuthResult)\n\n const res = await authMethodLdapService.validateUser('guest1', 'pass', '127.0.0.1')\n\n expect(res).toEqual(dbAuthResult)\n expect(usersManager.logUser).toHaveBeenCalledWith(guestUser, 'pass', '127.0.0.1')\n expect(Client).not.toHaveBeenCalled() // client should not be constructed\n })\n\n it('should throw FORBIDDEN for locked account and LDAP login mismatch', async () => {\n // Phase 1: locked account\n usersManager.findUser.mockResolvedValue({ login: 'john', isGuest: false, isActive: false } as UserModel)\n const loggerErrorSpy1 = jest.spyOn(authMethodLdapService['logger'], 'error').mockImplementation(() => undefined as any)\n\n await expect(authMethodLdapService.validateUser('john', 'pwd')).rejects.toThrow(/account locked/i)\n expect(loggerErrorSpy1).toHaveBeenCalled()\n\n // Phase 2: mismatch between requested login and LDAP returned login\n const existingUser: any = buildUser({ id: 8 })\n usersManager.findUser.mockResolvedValue(existingUser)\n const originalAttr = configuration.auth.ldap.loginAttribute\n configuration.auth.ldap.loginAttribute = 'cn'\n mockBindResolve(ldapClient)\n mockSearchEntries(ldapClient, [{ uid: 'jane', cn: 'john', mail: 'jane@example.org' }])\n\n await expect(authMethodLdapService.validateUser('john', 'pwd')).rejects.toThrow(/account matching error/i)\n\n // restore the attribute to avoid impacting other tests\n configuration.auth.ldap.loginAttribute = originalAttr\n })\n\n it('should handle invalid LDAP credentials for both existing and unknown users', async () => {\n // Phase 1: existing user -> updateAccesses invoked with success=false and logger.error intercepted\n const existingUser: any = buildUser({ id: 1 })\n usersManager.findUser.mockResolvedValue(existingUser)\n // Make LDAP bind throw InvalidCredentialsError\n mockBindRejectInvalid(ldapClient, InvalidCredentialsError, 'invalid credentials')\n // Force updateAccesses to reject to hit the catch and logger.error\n const loggerErrorSpy = jest.spyOn(authMethodLdapService['logger'], 'error').mockImplementation(() => undefined as any)\n usersManager.updateAccesses.mockRejectedValueOnce(new Error('updateAccesses boom'))\n\n const res1 = await authMethodLdapService.validateUser('john', 'badpwd', '10.0.0.1')\n\n expect(res1).toBeNull()\n expect(usersManager.updateAccesses).toHaveBeenCalledWith(existingUser, '10.0.0.1', false)\n expect(loggerErrorSpy).toHaveBeenCalled()\n\n // Phase 2: unknown user → no access update\n usersManager.updateAccesses.mockClear()\n usersManager.findUser.mockResolvedValue(null)\n ldapClient.bind.mockRejectedValue(new InvalidCredentialsError('invalid'))\n ldapClient.unbind.mockResolvedValue(undefined)\n\n const res2 = await authMethodLdapService.validateUser('jane', 'badpwd')\n\n expect(res2).toBeNull()\n expect(usersManager.updateAccesses).not.toHaveBeenCalled()\n })\n\n it('should handle LDAP new-user flow: missing fields, creation success, and multi-email selection', async () => {\n // Phase 1: incomplete LDAP entry -> null + error log, no creation\n usersManager.findUser.mockResolvedValue(null)\n mockBindResolve(ldapClient)\n // Simulate an entry with missing mail\n mockSearchEntries(ldapClient, [{ uid: 'jane', cn: 'Jane Doe', mail: undefined }])\n const loggerErrorSpy = jest.spyOn(authMethodLdapService['logger'], 'error').mockImplementation(() => undefined as any)\n\n const resA = await authMethodLdapService.validateUser('jane', 'pwd')\n\n expect(resA).toBeNull()\n expect(adminUsersManager.createUserOrGuest).not.toHaveBeenCalled()\n expect(loggerErrorSpy).toHaveBeenCalled()\n\n // Phase 2: create a new user (success, single email)\n usersManager.findUser.mockResolvedValue(null)\n setupLdapSuccess([{ uid: 'john', cn: 'John Doe', mail: 'john@example.org' }])\n\n const createdUser: any = { id: 2, login: 'john', isGuest: false, isActive: true, makePaths: jest.fn() }\n adminUsersManager.createUserOrGuest.mockResolvedValue(createdUser)\n // Cover the success-flow catch branch\n const loggerErrorSpy2 = spyLoggerError()\n usersManager.updateAccesses.mockRejectedValueOnce(new Error('updateAccesses success flow boom'))\n\n const resB = await authMethodLdapService.validateUser('john', 'pwd', '192.168.1.10')\n\n expect(adminUsersManager.createUserOrGuest).toHaveBeenCalledWith(\n { login: 'john', email: 'john@example.org', password: 'pwd', firstName: 'John', lastName: 'Doe' },\n expect.anything() // USER_ROLE.USER\n )\n expect(resB).toBe(createdUser)\n expect(usersManager.updateAccesses).toHaveBeenCalledWith(createdUser, '192.168.1.10', true)\n expect(loggerErrorSpy2).toHaveBeenCalled()\n\n // Phase 3: multiple emails -> keep the first\n usersManager.findUser.mockResolvedValue(null)\n setupLdapSuccess([{ uid: 'multi', cn: 'Multi Mail', mail: ['first@example.org', 'second@example.org'] }])\n\n const createdUser2: any = { id: 9, login: 'multi', makePaths: jest.fn() }\n adminUsersManager.createUserOrGuest.mockResolvedValue(createdUser2)\n\n const resC = await authMethodLdapService.validateUser('multi', 'pwd')\n\n expect(adminUsersManager.createUserOrGuest).toHaveBeenCalledWith(expect.objectContaining({ email: 'first@example.org' }), expect.anything())\n expect(resC).toBe(createdUser2)\n })\n\n it('should update existing user profile when LDAP identity changed (except password assigned back)', async () => {\n // Arrange: existing user with different profile and an old password\n const existingUser: any = buildUser({ id: 5 })\n usersManager.findUser.mockResolvedValue(existingUser)\n\n // LDAP succeeds and returns different email and same uid\n setupLdapSuccess([{ uid: 'john', cn: 'John Doe', mail: 'john@example.org' }])\n\n // Admin manager successfully updates a user\n adminUsersManager.updateUserOrGuest.mockResolvedValue(undefined)\n\n // Ensure password is considered changed so the update payload includes it,\n // which then triggers the deletion and local assignment branches after update\n const compareSpy = jest.spyOn(commonFunctions, 'comparePassword').mockResolvedValue(false)\n\n const res = await authMethodLdapService.validateUser('john', 'new-plain-password', '127.0.0.2')\n\n expect(adminUsersManager.updateUserOrGuest).toHaveBeenCalledWith(\n 5,\n expect.objectContaining({\n email: 'john@example.org',\n firstName: 'John',\n lastName: 'Doe'\n })\n )\n // Password should not be assigned back onto the user object (it is deleted before Object.assign)\n expect(existingUser.password).toBe('hashed')\n // Other fields should be updated locally\n expect(existingUser.email).toBe('john@example.org')\n expect(existingUser).toMatchObject({ firstName: 'John', lastName: 'Doe' })\n // Accesses updated as success\n expect(usersManager.updateAccesses).toHaveBeenCalledWith(existingUser, '127.0.0.2', true)\n // Returned user is the same instance\n expect(res).toBe(existingUser)\n\n // Second run: password unchanged (comparePassword => true) to cover the null branch for password\n adminUsersManager.updateUserOrGuest.mockClear()\n usersManager.updateAccesses.mockClear()\n // Force another non-password change so an update occurs\n existingUser.email = 'old@example.org'\n compareSpy.mockResolvedValue(true)\n\n const res2 = await authMethodLdapService.validateUser('john', 'same-plain-password', '127.0.0.3')\n\n // Update should be called without password, only with changed fields\n expect(adminUsersManager.updateUserOrGuest).toHaveBeenCalled()\n const updateArgs = adminUsersManager.updateUserOrGuest.mock.calls[0]\n expect(updateArgs[0]).toBe(5)\n expect(updateArgs[1]).toEqual(\n expect.objectContaining({\n email: 'john@example.org'\n })\n )\n expect(updateArgs[1]).toEqual(expect.not.objectContaining({ password: expect.anything() }))\n\n // Password remains unchanged locally\n expect(existingUser.password).toBe('hashed')\n // Accesses updated as success\n expect(usersManager.updateAccesses).toHaveBeenCalledWith(existingUser, '127.0.0.3', true)\n // Returned user is the same instance\n expect(res2).toBe(existingUser)\n\n // Third run: no changes at all (identityHasChanged is empty) to cover the else branch\n adminUsersManager.updateUserOrGuest.mockClear()\n usersManager.updateAccesses.mockClear()\n compareSpy.mockResolvedValue(true)\n\n // Local user already matches LDAP identity; call again\n const res3 = await authMethodLdapService.validateUser('john', 'same-plain-password', '127.0.0.4')\n\n // No update should be triggered\n expect(adminUsersManager.updateUserOrGuest).not.toHaveBeenCalled()\n // Access should still be updated as success\n expect(usersManager.updateAccesses).toHaveBeenCalledWith(existingUser, '127.0.0.4', true)\n // Returned user is the same instance\n expect(res3).toBe(existingUser)\n })\n\n it('should log failed access when LDAP search returns no entry or throws after bind', async () => {\n // Phase 1: no entry found after a successful bind -> failed access\n const existingUser: any = { id: 7, login: 'ghost', isGuest: false, isActive: true }\n usersManager.findUser.mockResolvedValue(existingUser)\n setupLdapSuccess([])\n\n const resA = await authMethodLdapService.validateUser('ghost', 'pwd', '10.10.0.1')\n\n expect(resA).toBeNull()\n expect(usersManager.updateAccesses).toHaveBeenCalledWith(existingUser, '10.10.0.1', false)\n\n // Phase 2: exception during search after a bind -> failed access\n jest.clearAllMocks()\n const existingUser2: any = { id: 10, login: 'john', isGuest: false, isActive: true }\n usersManager.findUser.mockResolvedValue(existingUser2)\n mockBindResolve(ldapClient)\n mockSearchReject(ldapClient, new Error('search failed'))\n\n const resB = await authMethodLdapService.validateUser('john', 'pwd', '1.1.1.1')\n\n expect(resB).toBeNull()\n expect(usersManager.updateAccesses).toHaveBeenCalledWith(existingUser2, '1.1.1.1', false)\n })\n\n it('should throw 500 when LDAP connection error occurs during bind', async () => {\n // Arrange: no existing user to reach checkAuth flow\n usersManager.findUser.mockResolvedValue(null)\n const err1 = new Error('socket hang up')\n const err2 = Object.assign(new Error('connect ECONNREFUSED'), { code: Array.from(CONNECT_ERROR_CODE)[0] })\n ldapClient.bind.mockRejectedValue({ errors: [err1, err2] })\n ldapClient.unbind.mockResolvedValue(undefined)\n\n // First scenario: recognized connection error -> throws 500\n await expect(authMethodLdapService.validateUser('john', 'pwd')).rejects.toThrow(/authentication service/i)\n\n // Second scenario: generic error (no code, not InvalidCredentialsError) -> resolves to null and no access update\n ldapClient.bind.mockReset()\n ldapClient.unbind.mockReset()\n usersManager.updateAccesses.mockClear()\n usersManager.findUser.mockResolvedValue(null as any)\n ldapClient.bind.mockRejectedValue(new Error('unexpected failure'))\n ldapClient.unbind.mockResolvedValue(undefined)\n\n const res = await authMethodLdapService.validateUser('john', 'pwd')\n expect(res).toBeNull()\n expect(usersManager.updateAccesses).not.toHaveBeenCalled()\n })\n\n it('should log update failure and still call makePaths when updating existing user', async () => {\n // Arrange: existing user with changed identity\n const existingUser: any = buildUser({ id: 11, email: 'old@ex.org' })\n usersManager.findUser.mockResolvedValue(existingUser)\n\n // Ensure LDAP loginAttribute matches uid for this test (a previous test sets it to 'cn')\n configuration.auth.ldap.loginAttribute = 'uid'\n\n setupLdapSuccess([{ uid: 'john', cn: 'John Doe', mail: 'john@example.org' }])\n adminUsersManager.updateUserOrGuest.mockRejectedValue(new Error('db error'))\n\n // Force identity to be considered changed only for this test\n jest.spyOn(commonFunctions, 'comparePassword').mockResolvedValue(false)\n jest.spyOn(commonFunctions, 'splitFullName').mockReturnValue({ firstName: 'John', lastName: 'Doe' })\n\n const res = await authMethodLdapService.validateUser('john', 'pwd')\n\n expect(adminUsersManager.updateUserOrGuest).toHaveBeenCalled()\n // makePaths still invoked\n expect(existingUser.makePaths).toHaveBeenCalled()\n // Local fields unchanged since update failed\n expect(existingUser.email).toBe('old@ex.org')\n expect(res).toBe(existingUser)\n })\n\n it('should skip non-matching LDAP entries then update user with changed password without reassigning it', async () => {\n // Phase A: LDAP returns an entry but loginAttribute value does not match -> checkAccess returns false (covers return after loop)\n const userA: any = { id: 20, login: 'john', isGuest: false, isActive: true }\n usersManager.findUser.mockResolvedValue(userA)\n configuration.auth.ldap.loginAttribute = 'uid'\n ldapClient.bind.mockResolvedValue(undefined)\n // Non-matching entry: uid !== requested uid\n ldapClient.search.mockResolvedValue({ searchEntries: [{ uid: 'jane', cn: 'Jane Doe', mail: 'jane@example.org' }] })\n ldapClient.unbind.mockResolvedValue(undefined)\n\n const resA = await authMethodLdapService.validateUser('john', 'pwd', '3.3.3.3')\n expect(resA).toBeNull()\n expect(usersManager.updateAccesses).toHaveBeenCalledWith(userA, '3.3.3.3', false)\n\n // Phase B: Matching entry + password considered changed -> updateUserOrGuest called, password not reassigned locally\n jest.clearAllMocks()\n const userB: any = buildUser({ id: 21, email: 'old@ex.org' })\n usersManager.findUser.mockResolvedValue(userB)\n configuration.auth.ldap.loginAttribute = 'uid'\n setupLdapSuccess([{ uid: 'john', cn: 'John Doe', mail: 'john@example.org' }])\n adminUsersManager.updateUserOrGuest.mockResolvedValue(undefined)\n\n // Force password to be considered changed to execute deletion + Object.assign branch\n jest.spyOn(commonFunctions, 'comparePassword').mockResolvedValue(false)\n jest.spyOn(commonFunctions, 'splitFullName').mockReturnValue({ firstName: 'John', lastName: 'Doe' })\n\n const resB = await authMethodLdapService.validateUser('john', 'newpwd', '4.4.4.4')\n\n // Line 132: updateUserOrGuest call\n expect(adminUsersManager.updateUserOrGuest).toHaveBeenCalledWith(\n 21,\n expect.objectContaining({ email: 'john@example.org', firstName: 'John', lastName: 'Doe' })\n )\n // Lines 139-142: password removed from local assign, other fields assigned\n expect(userB.password).toBe('hashed')\n expect(userB.email).toBe('john@example.org')\n expect(userB).toMatchObject({ firstName: 'John', lastName: 'Doe' })\n expect(userB.makePaths).toHaveBeenCalled()\n expect(resB).toBe(userB)\n })\n})\n"],"names":["jest","mock","InvalidCredentialsError","Error","mockClientInstance","bind","fn","search","unbind","Client","mockImplementation","mockBindResolve","ldapClient","mockResolvedValue","undefined","mockBindRejectInvalid","InvalidCredentialsErrorCtor","message","mockRejectedValue","mockSearchEntries","entries","searchEntries","mockSearchReject","err","buildUser","overrides","id","login","email","password","isGuest","isActive","makePaths","describe","AuthMethodLdapService","name","authMethodLdapService","usersManager","adminUsersManager","setupLdapSuccess","spyLoggerError","spyOn","beforeAll","module","Test","createTestingModule","providers","provide","UsersManager","useValue","findUser","logUser","updateAccesses","AdminUsersManager","createUserOrGuest","updateUserOrGuest","compile","useLogger","get","configuration","auth","ldap","servers","loginAttribute","baseDN","filter","it","expect","toBeDefined","guestUser","dbAuthResult","token","res","validateUser","toEqual","toHaveBeenCalledWith","not","toHaveBeenCalled","loggerErrorSpy1","rejects","toThrow","existingUser","originalAttr","uid","cn","mail","loggerErrorSpy","mockRejectedValueOnce","res1","toBeNull","mockClear","res2","resA","createdUser","loggerErrorSpy2","resB","firstName","lastName","anything","toBe","createdUser2","resC","objectContaining","compareSpy","commonFunctions","toMatchObject","updateArgs","calls","res3","clearAllMocks","existingUser2","err1","err2","Object","assign","code","Array","from","CONNECT_ERROR_CODE","errors","mockReset","mockReturnValue","userA","userB"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../backend/src/authentication/services/auth-methods/auth-method-ldap.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 { Mocked } from 'jest-mock'\nimport { Client, InvalidCredentialsError } from 'ldapts'\nimport { CONNECT_ERROR_CODE } from '../../../app.constants'\nimport { UserModel } from '../../../applications/users/models/user.model'\nimport { AdminUsersManager } from '../../../applications/users/services/admin-users-manager.service'\nimport { UsersManager } from '../../../applications/users/services/users-manager.service'\nimport * as commonFunctions from '../../../common/functions'\nimport { configuration } from '../../../configuration/config.environment'\nimport { LDAP_LOGIN_ATTR } from '../../constants/auth-ldap'\nimport { AuthMethodLdapService } from './auth-method-ldap.service'\n\n// Mock ldapts Client to simulate LDAP behaviors\njest.mock('ldapts', () => {\n const actual = jest.requireActual('ldapts')\n const mockClientInstance = {\n bind: jest.fn(),\n search: jest.fn(),\n unbind: jest.fn()\n }\n const Client = jest.fn().mockImplementation(() => mockClientInstance)\n // Conserver tous les autres exports réels (dont EqualityFilter, AndFilter, InvalidCredentialsError, etc.)\n return { ...actual, Client }\n})\n\n// --- Test helpers (DRY) ---\n// Reusable LDAP mocks\nconst mockBindResolve = (ldapClient: any) => {\n ldapClient.bind.mockResolvedValue(undefined)\n ldapClient.unbind.mockResolvedValue(undefined)\n}\nconst mockBindRejectInvalid = (ldapClient: any, InvalidCredentialsErrorCtor: any, message = 'invalid') => {\n ldapClient.bind.mockRejectedValue(new InvalidCredentialsErrorCtor(message))\n ldapClient.unbind.mockResolvedValue(undefined)\n}\nconst mockSearchEntries = (ldapClient: any, entries: any[]) => {\n ldapClient.search.mockResolvedValue({ searchEntries: entries })\n}\nconst mockSearchReject = (ldapClient: any, err: Error) => {\n ldapClient.search.mockRejectedValue(err)\n}\n// User factory\nconst buildUser = (overrides: Partial<UserModel> = {}) =>\n ({\n id: 0,\n login: 'john',\n email: 'old@example.org',\n password: 'hashed',\n isGuest: false,\n isActive: true,\n makePaths: jest.fn().mockResolvedValue(undefined),\n setFullName: jest.fn(), // needed when firstName/lastName change\n ...overrides\n }) as any\n\n// --------------------------\n\ndescribe(AuthMethodLdapService.name, () => {\n let authMethodLdapService: AuthMethodLdapService\n let usersManager: Mocked<UsersManager>\n let adminUsersManager: Mocked<AdminUsersManager>\n const ldapClient = {\n bind: jest.fn(),\n search: jest.fn(),\n unbind: jest.fn()\n }\n ;(Client as Mocked<any>).mockImplementation(() => ldapClient)\n\n // Local helpers (need access to authMethodLdapService and ldapClient in this scope)\n const setupLdapSuccess = (entries: any[]) => {\n mockBindResolve(ldapClient)\n mockSearchEntries(ldapClient, entries)\n }\n const spyLoggerError = () => jest.spyOn(authMethodLdapService['logger'], 'error').mockImplementation(() => undefined as any)\n\n beforeAll(async () => {\n configuration.auth.ldap = {\n servers: ['ldap://localhost:389'],\n attributes: { login: LDAP_LOGIN_ATTR.UID, email: 'mail' },\n baseDN: 'ou=people,dc=example,dc=org',\n filter: ''\n }\n\n const module: TestingModule = await Test.createTestingModule({\n providers: [\n AuthMethodLdapService,\n {\n provide: UsersManager,\n useValue: {\n findUser: jest.fn(),\n logUser: jest.fn(),\n updateAccesses: jest.fn().mockResolvedValue(undefined),\n validateAppPassword: jest.fn(),\n fromUserId: jest.fn()\n }\n },\n {\n provide: AdminUsersManager,\n useValue: {\n createUserOrGuest: jest.fn(),\n updateUserOrGuest: jest.fn()\n }\n }\n ]\n }).compile()\n\n module.useLogger(['fatal'])\n authMethodLdapService = module.get<AuthMethodLdapService>(AuthMethodLdapService)\n adminUsersManager = module.get<Mocked<AdminUsersManager>>(AdminUsersManager)\n usersManager = module.get<Mocked<UsersManager>>(UsersManager)\n })\n\n it('should be defined', () => {\n expect(authMethodLdapService).toBeDefined()\n expect(usersManager).toBeDefined()\n expect(adminUsersManager).toBeDefined()\n expect(ldapClient).toBeDefined()\n })\n\n it('should authenticate a guest user via database and bypass LDAP', async () => {\n // Arrange\n const guestUser: any = { id: 1, login: 'guest1', isGuest: true, isActive: true }\n usersManager.findUser.mockResolvedValue(guestUser)\n const dbAuthResult: any = { ...guestUser, token: 'jwt' }\n usersManager.logUser.mockResolvedValue(dbAuthResult)\n const res = await authMethodLdapService.validateUser('guest1', 'pass', '127.0.0.1')\n expect(res).toEqual(dbAuthResult)\n expect(usersManager.logUser).toHaveBeenCalledWith(guestUser, 'pass', '127.0.0.1')\n expect(Client).not.toHaveBeenCalled() // client should not be constructed\n })\n\n it('should throw FORBIDDEN for locked account and resolve null for LDAP login mismatch', async () => {\n // Phase 1: locked account\n usersManager.findUser.mockResolvedValue({ login: 'john', isGuest: false, isActive: false } as UserModel)\n const loggerErrorSpy1 = jest.spyOn(authMethodLdapService['logger'], 'error').mockImplementation(() => undefined as any)\n await expect(authMethodLdapService.validateUser('john', 'pwd')).rejects.toThrow(/account locked/i)\n expect(loggerErrorSpy1).toHaveBeenCalled()\n\n // Phase 2: mismatch between requested login and LDAP returned login -> service renvoie null\n const existingUser: any = buildUser({ id: 8 })\n usersManager.findUser.mockResolvedValue(existingUser)\n mockBindResolve(ldapClient)\n mockSearchEntries(ldapClient, [{ uid: 'jane', cn: 'john', mail: 'jane@example.org' }])\n await expect(authMethodLdapService.validateUser('john', 'pwd')).rejects.toThrow(/account matching error/i)\n })\n\n it('should handle invalid LDAP credentials for both existing and unknown users', async () => {\n // Phase 1: existing user -> updateAccesses invoked with success=false and logger.error intercepted\n const existingUser: any = buildUser({ id: 1 })\n usersManager.findUser.mockResolvedValue(existingUser)\n // Make LDAP bind throw InvalidCredentialsError\n mockBindRejectInvalid(ldapClient, InvalidCredentialsError, 'invalid credentials')\n // Force updateAccesses to reject to hit the catch and logger.error\n const loggerErrorSpy = jest.spyOn(authMethodLdapService['logger'], 'error').mockImplementation(() => undefined as any)\n usersManager.updateAccesses.mockRejectedValueOnce(new Error('updateAccesses boom'))\n const res1 = await authMethodLdapService.validateUser('john', 'badpwd', '10.0.0.1')\n expect(res1).toBeNull()\n expect(usersManager.updateAccesses).toHaveBeenCalledWith(existingUser, '10.0.0.1', false)\n expect(loggerErrorSpy).toHaveBeenCalled()\n\n // Phase 2: unknown user → no access update\n usersManager.updateAccesses.mockClear()\n usersManager.findUser.mockResolvedValue(null)\n ldapClient.bind.mockRejectedValue(new InvalidCredentialsError('invalid'))\n ldapClient.unbind.mockResolvedValue(undefined)\n const res2 = await authMethodLdapService.validateUser('jane', 'badpwd')\n expect(res2).toBeNull()\n expect(usersManager.updateAccesses).not.toHaveBeenCalled()\n })\n\n it('should handle LDAP new-user flow: missing fields, creation success, and multi-email selection', async () => {\n // Phase 1: incomplete LDAP entry -> null + error log, no creation\n usersManager.findUser.mockResolvedValue(null)\n mockBindResolve(ldapClient)\n // Simulate an entry with missing mail\n mockSearchEntries(ldapClient, [{ uid: 'jane', cn: 'Jane Doe', mail: undefined }])\n const loggerErrorSpy = jest.spyOn(authMethodLdapService['logger'], 'error').mockImplementation(() => undefined as any)\n const resA = await authMethodLdapService.validateUser('jane', 'pwd')\n expect(resA).toBeNull()\n expect(adminUsersManager.createUserOrGuest).not.toHaveBeenCalled()\n expect(loggerErrorSpy).toHaveBeenCalled()\n\n // Phase 2: create a new user (success, single email)\n // Stub directement checkAuth pour retourner une entrée LDAP valide\n const checkAuthSpy = jest.spyOn<any, any>(authMethodLdapService as any, 'checkAuth')\n checkAuthSpy.mockResolvedValueOnce({ uid: 'john', cn: 'John Doe', mail: 'john@example.org' } as any)\n adminUsersManager.createUserOrGuest.mockClear()\n usersManager.findUser.mockResolvedValue(null)\n const createdUser: any = { id: 2, login: 'john', isGuest: false, isActive: true, makePaths: jest.fn() }\n adminUsersManager.createUserOrGuest.mockResolvedValue(createdUser)\n // If the service reloads the user via fromUserId after creation\n usersManager.fromUserId.mockResolvedValue(createdUser)\n // Cover the success-flow catch branch\n const loggerErrorSpy2 = spyLoggerError()\n usersManager.updateAccesses.mockRejectedValueOnce(new Error('updateAccesses success flow boom'))\n const resB = await authMethodLdapService.validateUser('john', 'pwd', '192.168.1.10')\n expect(adminUsersManager.createUserOrGuest).toHaveBeenCalledWith(\n { login: 'john', email: 'john@example.org', password: 'pwd', firstName: 'John', lastName: 'Doe' },\n expect.anything() // USER_ROLE.USER\n )\n expect(resB).toBe(createdUser)\n expect(usersManager.updateAccesses).toHaveBeenCalledWith(createdUser, '192.168.1.10', true)\n expect(loggerErrorSpy2).toHaveBeenCalled()\n // Phase 3: multiple emails -> keep the first\n adminUsersManager.createUserOrGuest.mockClear()\n usersManager.findUser.mockResolvedValue(null)\n setupLdapSuccess([{ uid: 'multi', cn: 'Multi Mail', mail: ['first@example.org', 'second@example.org'] }])\n const createdUser2: any = { id: 9, login: 'multi', makePaths: jest.fn() }\n adminUsersManager.createUserOrGuest.mockResolvedValue(createdUser2)\n usersManager.fromUserId.mockResolvedValue(createdUser2)\n const resC = await authMethodLdapService.validateUser('multi', 'pwd')\n expect(adminUsersManager.createUserOrGuest).toHaveBeenCalledWith(expect.objectContaining({ email: 'first@example.org' }), expect.anything())\n expect(resC).toBe(createdUser2)\n })\n\n it('should update existing user profile when LDAP identity changed (except password assigned back)', async () => {\n // Arrange: existing user with different profile and an old password\n const existingUser: any = buildUser({ id: 5 })\n usersManager.findUser.mockResolvedValue(existingUser)\n // LDAP succeeds and returns different email and same uid\n setupLdapSuccess([{ uid: 'john', cn: 'John Doe', mail: 'john@example.org' }])\n // Admin manager successfully updates a user\n adminUsersManager.updateUserOrGuest.mockResolvedValue(undefined)\n // Ensure password is considered changed so the update payload includes it,\n // which then triggers the deletion and local assignment branches after update\n const compareSpy = jest.spyOn(commonFunctions, 'comparePassword').mockResolvedValue(false)\n const res = await authMethodLdapService.validateUser('john', 'new-plain-password', '127.0.0.2')\n expect(adminUsersManager.updateUserOrGuest).toHaveBeenCalledWith(\n 5,\n expect.objectContaining({\n email: 'john@example.org',\n firstName: 'John',\n lastName: 'Doe'\n })\n )\n // Password should not be assigned back onto the user object (it is deleted before Object.assign)\n expect(existingUser.password).toBe('hashed')\n // Other fields should be updated locally\n expect(existingUser.email).toBe('john@example.org')\n expect(existingUser).toMatchObject({ firstName: 'John', lastName: 'Doe' })\n // Accesses updated as success\n expect(usersManager.updateAccesses).toHaveBeenCalledWith(existingUser, '127.0.0.2', true)\n // Returned user is the same instance\n expect(res).toBe(existingUser)\n\n // Second run: password unchanged (comparePassword => true) to cover the null branch for password\n adminUsersManager.updateUserOrGuest.mockClear()\n usersManager.updateAccesses.mockClear()\n // Force another non-password change so an update occurs\n existingUser.email = 'old@example.org'\n compareSpy.mockResolvedValue(true)\n const res2 = await authMethodLdapService.validateUser('john', 'same-plain-password', '127.0.0.3')\n // Update should be called without password, only with changed fields\n expect(adminUsersManager.updateUserOrGuest).toHaveBeenCalled()\n const updateArgs = adminUsersManager.updateUserOrGuest.mock.calls[0]\n expect(updateArgs[0]).toBe(5)\n expect(updateArgs[1]).toEqual(\n expect.objectContaining({\n email: 'john@example.org'\n })\n )\n expect(updateArgs[1]).toEqual(expect.not.objectContaining({ password: expect.anything() }))\n // Password remains unchanged locally\n expect(existingUser.password).toBe('hashed')\n // Accesses updated as success\n expect(usersManager.updateAccesses).toHaveBeenCalledWith(existingUser, '127.0.0.3', true)\n // Returned user is the same instance\n expect(res2).toBe(existingUser)\n // Third run: no changes at all (identityHasChanged is empty) to cover the else branch\n adminUsersManager.updateUserOrGuest.mockClear()\n usersManager.updateAccesses.mockClear()\n compareSpy.mockResolvedValue(true)\n // Local user already matches LDAP identity; call again\n const res3 = await authMethodLdapService.validateUser('john', 'same-plain-password', '127.0.0.4')\n // No update should be triggered\n expect(adminUsersManager.updateUserOrGuest).not.toHaveBeenCalled()\n // Access should still be updated as success\n expect(usersManager.updateAccesses).toHaveBeenCalledWith(existingUser, '127.0.0.4', true)\n // Returned user is the same instance\n expect(res3).toBe(existingUser)\n })\n\n it('should log failed access when LDAP search returns no entry or throws after bind', async () => {\n // Phase 1: no entry found after a successful bind -> failed access\n const existingUser: any = { id: 7, login: 'ghost', isGuest: false, isActive: true }\n usersManager.findUser.mockResolvedValue(existingUser)\n setupLdapSuccess([])\n const resA = await authMethodLdapService.validateUser('ghost', 'pwd', '10.10.0.1')\n expect(resA).toBeNull()\n expect(usersManager.updateAccesses).toHaveBeenCalledWith(existingUser, '10.10.0.1', false)\n\n // Phase 2: exception during search after a bind -> failed access\n jest.clearAllMocks()\n const existingUser2: any = { id: 10, login: 'john', isGuest: false, isActive: true }\n usersManager.findUser.mockResolvedValue(existingUser2)\n mockBindResolve(ldapClient)\n mockSearchReject(ldapClient, new Error('search failed'))\n const resB = await authMethodLdapService.validateUser('john', 'pwd', '1.1.1.1')\n expect(resB).toBeNull()\n expect(usersManager.updateAccesses).toHaveBeenCalledWith(existingUser2, '1.1.1.1', false)\n })\n\n it('should allow app password when LDAP fails and scope is provided', async () => {\n const existingUser: any = buildUser({ id: 42 })\n usersManager.findUser.mockResolvedValue(existingUser)\n // LDAP invalid credentials\n mockBindRejectInvalid(ldapClient, InvalidCredentialsError, 'invalid credentials')\n // App password success\n usersManager.validateAppPassword.mockResolvedValue(true)\n const res = await authMethodLdapService.validateUser('john', 'app-password', '10.0.0.2', 'webdav' as any)\n expect(res).toBe(existingUser)\n expect(usersManager.validateAppPassword).toHaveBeenCalledWith(existingUser, 'app-password', '10.0.0.2', 'webdav')\n expect(usersManager.updateAccesses).toHaveBeenCalledWith(existingUser, '10.0.0.2', true)\n })\n\n it('should throw 500 when LDAP connection error occurs during bind', async () => {\n // Arrange: no existing user to reach checkAuth flow\n usersManager.findUser.mockResolvedValue(null)\n const err1 = new Error('socket hang up')\n const err2 = Object.assign(new Error('connect ECONNREFUSED'), { code: Array.from(CONNECT_ERROR_CODE)[0] })\n ldapClient.bind.mockRejectedValue({ errors: [err1, err2] })\n ldapClient.unbind.mockResolvedValue(undefined)\n\n // First scenario: recognized connection error -> throws 500\n await expect(authMethodLdapService.validateUser('john', 'pwd')).rejects.toThrow(/authentication service/i)\n\n // Second scenario: generic error (no code, not InvalidCredentialsError) -> resolves to null and no access update\n ldapClient.bind.mockReset()\n ldapClient.unbind.mockReset()\n usersManager.updateAccesses.mockClear()\n usersManager.findUser.mockResolvedValue(null as any)\n ldapClient.bind.mockRejectedValue(new Error('unexpected failure'))\n ldapClient.unbind.mockResolvedValue(undefined)\n\n const res = await authMethodLdapService.validateUser('john', 'pwd')\n expect(res).toBeNull()\n expect(usersManager.updateAccesses).not.toHaveBeenCalled()\n })\n\n it('should log update failure when updating existing user', async () => {\n // Arrange: existing user with changed identity\n const existingUser: any = buildUser({ id: 11, email: 'old@ex.org' })\n usersManager.findUser.mockResolvedValue(existingUser)\n // Ensure LDAP loginAttribute matches uid for this test (a previous test sets it to 'cn')\n setupLdapSuccess([{ uid: 'john', cn: 'John Doe', mail: 'john@example.org' }])\n adminUsersManager.updateUserOrGuest.mockRejectedValue(new Error('db error'))\n // Force identity to be considered changed only for this test\n jest.spyOn(commonFunctions, 'comparePassword').mockResolvedValue(false)\n jest.spyOn(commonFunctions, 'splitFullName').mockReturnValue({ firstName: 'John', lastName: 'Doe' })\n const res = await authMethodLdapService.validateUser('john', 'pwd')\n expect(adminUsersManager.updateUserOrGuest).toHaveBeenCalled()\n // Local fields unchanged since update failed\n expect(existingUser.email).toBe('old@ex.org')\n expect(res).toBe(existingUser)\n })\n\n it('should skip non-matching LDAP entries then update user with changed password without reassigning it', async () => {\n // Phase A: LDAP returns an entry but loginAttribute value does not match -> checkAccess returns false (covers return after loop)\n const userA: any = { id: 20, login: 'john', isGuest: false, isActive: true }\n usersManager.findUser.mockResolvedValue(userA)\n ldapClient.bind.mockResolvedValue(undefined)\n\n // Phase B: Matching entry + password considered changed -> updateUserOrGuest called, password not reassigned locally\n jest.clearAllMocks()\n const userB: any = buildUser({ id: 21, email: 'old@ex.org' })\n usersManager.findUser.mockResolvedValue(userB)\n setupLdapSuccess([{ uid: 'john', cn: 'John Doe', mail: 'john@example.org' }])\n adminUsersManager.updateUserOrGuest.mockResolvedValue(undefined)\n\n // Force password to be considered changed to execute deletion + Object.assign branch\n jest.spyOn(commonFunctions, 'comparePassword').mockResolvedValue(false)\n jest.spyOn(commonFunctions, 'splitFullName').mockReturnValue({ firstName: 'John', lastName: 'Doe' })\n const resB = await authMethodLdapService.validateUser('john', 'newpwd', '4.4.4.4')\n\n // Line 132: updateUserOrGuest call\n expect(adminUsersManager.updateUserOrGuest).toHaveBeenCalledWith(\n 21,\n expect.objectContaining({ email: 'john@example.org', firstName: 'John', lastName: 'Doe' })\n )\n\n // Lines 139-142: password removed from local assign, other fields assigned\n expect(userB.password).toBe('hashed')\n expect(userB.email).toBe('john@example.org')\n expect(userB).toMatchObject({ firstName: 'John', lastName: 'Doe' })\n expect(resB).toBe(userB)\n })\n})\n"],"names":["jest","mock","actual","requireActual","mockClientInstance","bind","fn","search","unbind","Client","mockImplementation","mockBindResolve","ldapClient","mockResolvedValue","undefined","mockBindRejectInvalid","InvalidCredentialsErrorCtor","message","mockRejectedValue","mockSearchEntries","entries","searchEntries","mockSearchReject","err","buildUser","overrides","id","login","email","password","isGuest","isActive","makePaths","setFullName","describe","AuthMethodLdapService","name","authMethodLdapService","usersManager","adminUsersManager","setupLdapSuccess","spyLoggerError","spyOn","beforeAll","configuration","auth","ldap","servers","attributes","LDAP_LOGIN_ATTR","UID","baseDN","filter","module","Test","createTestingModule","providers","provide","UsersManager","useValue","findUser","logUser","updateAccesses","validateAppPassword","fromUserId","AdminUsersManager","createUserOrGuest","updateUserOrGuest","compile","useLogger","get","it","expect","toBeDefined","guestUser","dbAuthResult","token","res","validateUser","toEqual","toHaveBeenCalledWith","not","toHaveBeenCalled","loggerErrorSpy1","rejects","toThrow","existingUser","uid","cn","mail","InvalidCredentialsError","loggerErrorSpy","mockRejectedValueOnce","Error","res1","toBeNull","mockClear","res2","resA","checkAuthSpy","mockResolvedValueOnce","createdUser","loggerErrorSpy2","resB","firstName","lastName","anything","toBe","createdUser2","resC","objectContaining","compareSpy","commonFunctions","toMatchObject","updateArgs","calls","res3","clearAllMocks","existingUser2","err1","err2","Object","assign","code","Array","from","CONNECT_ERROR_CODE","errors","mockReset","mockReturnValue","userA","userB"],"mappings":"AAAA;;;;CAIC;;;;yBAEmC;wBAEY;8BACb;0CAED;qCACL;mEACI;mCACH;0BACE;uCACM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEtC,gDAAgD;AAChDA,KAAKC,IAAI,CAAC,UAAU;IAClB,MAAMC,SAASF,KAAKG,aAAa,CAAC;IAClC,MAAMC,qBAAqB;QACzBC,MAAML,KAAKM,EAAE;QACbC,QAAQP,KAAKM,EAAE;QACfE,QAAQR,KAAKM,EAAE;IACjB;IACA,MAAMG,SAAST,KAAKM,EAAE,GAAGI,kBAAkB,CAAC,IAAMN;IAClD,0GAA0G;IAC1G,OAAO;QAAE,GAAGF,MAAM;QAAEO;IAAO;AAC7B;AAEA,6BAA6B;AAC7B,sBAAsB;AACtB,MAAME,kBAAkB,CAACC;IACvBA,WAAWP,IAAI,CAACQ,iBAAiB,CAACC;IAClCF,WAAWJ,MAAM,CAACK,iBAAiB,CAACC;AACtC;AACA,MAAMC,wBAAwB,CAACH,YAAiBI,6BAAkCC,UAAU,SAAS;IACnGL,WAAWP,IAAI,CAACa,iBAAiB,CAAC,IAAIF,4BAA4BC;IAClEL,WAAWJ,MAAM,CAACK,iBAAiB,CAACC;AACtC;AACA,MAAMK,oBAAoB,CAACP,YAAiBQ;IAC1CR,WAAWL,MAAM,CAACM,iBAAiB,CAAC;QAAEQ,eAAeD;IAAQ;AAC/D;AACA,MAAME,mBAAmB,CAACV,YAAiBW;IACzCX,WAAWL,MAAM,CAACW,iBAAiB,CAACK;AACtC;AACA,eAAe;AACf,MAAMC,YAAY,CAACC,YAAgC,CAAC,CAAC,GAClD,CAAA;QACCC,IAAI;QACJC,OAAO;QACPC,OAAO;QACPC,UAAU;QACVC,SAAS;QACTC,UAAU;QACVC,WAAWhC,KAAKM,EAAE,GAAGO,iBAAiB,CAACC;QACvCmB,aAAajC,KAAKM,EAAE;QACpB,GAAGmB,SAAS;IACd,CAAA;AAEF,6BAA6B;AAE7BS,SAASC,4CAAqB,CAACC,IAAI,EAAE;IACnC,IAAIC;IACJ,IAAIC;IACJ,IAAIC;IACJ,MAAM3B,aAAa;QACjBP,MAAML,KAAKM,EAAE;QACbC,QAAQP,KAAKM,EAAE;QACfE,QAAQR,KAAKM,EAAE;IACjB;IACEG,cAAM,CAAiBC,kBAAkB,CAAC,IAAME;IAElD,oFAAoF;IACpF,MAAM4B,mBAAmB,CAACpB;QACxBT,gBAAgBC;QAChBO,kBAAkBP,YAAYQ;IAChC;IACA,MAAMqB,iBAAiB,IAAMzC,KAAK0C,KAAK,CAACL,qBAAqB,CAAC,SAAS,EAAE,SAAS3B,kBAAkB,CAAC,IAAMI;IAE3G6B,UAAU;QACRC,gCAAa,CAACC,IAAI,CAACC,IAAI,GAAG;YACxBC,SAAS;gBAAC;aAAuB;YACjCC,YAAY;gBAAErB,OAAOsB,yBAAe,CAACC,GAAG;gBAAEtB,OAAO;YAAO;YACxDuB,QAAQ;YACRC,QAAQ;QACV;QAEA,MAAMC,SAAwB,MAAMC,aAAI,CAACC,mBAAmB,CAAC;YAC3DC,WAAW;gBACTrB,4CAAqB;gBACrB;oBACEsB,SAASC,iCAAY;oBACrBC,UAAU;wBACRC,UAAU5D,KAAKM,EAAE;wBACjBuD,SAAS7D,KAAKM,EAAE;wBAChBwD,gBAAgB9D,KAAKM,EAAE,GAAGO,iBAAiB,CAACC;wBAC5CiD,qBAAqB/D,KAAKM,EAAE;wBAC5B0D,YAAYhE,KAAKM,EAAE;oBACrB;gBACF;gBACA;oBACEmD,SAASQ,2CAAiB;oBAC1BN,UAAU;wBACRO,mBAAmBlE,KAAKM,EAAE;wBAC1B6D,mBAAmBnE,KAAKM,EAAE;oBAC5B;gBACF;aACD;QACH,GAAG8D,OAAO;QAEVf,OAAOgB,SAAS,CAAC;YAAC;SAAQ;QAC1BhC,wBAAwBgB,OAAOiB,GAAG,CAAwBnC,4CAAqB;QAC/EI,oBAAoBc,OAAOiB,GAAG,CAA4BL,2CAAiB;QAC3E3B,eAAee,OAAOiB,GAAG,CAAuBZ,iCAAY;IAC9D;IAEAa,GAAG,qBAAqB;QACtBC,OAAOnC,uBAAuBoC,WAAW;QACzCD,OAAOlC,cAAcmC,WAAW;QAChCD,OAAOjC,mBAAmBkC,WAAW;QACrCD,OAAO5D,YAAY6D,WAAW;IAChC;IAEAF,GAAG,iEAAiE;QAClE,UAAU;QACV,MAAMG,YAAiB;YAAEhD,IAAI;YAAGC,OAAO;YAAUG,SAAS;YAAMC,UAAU;QAAK;QAC/EO,aAAasB,QAAQ,CAAC/C,iBAAiB,CAAC6D;QACxC,MAAMC,eAAoB;YAAE,GAAGD,SAAS;YAAEE,OAAO;QAAM;QACvDtC,aAAauB,OAAO,CAAChD,iBAAiB,CAAC8D;QACvC,MAAME,MAAM,MAAMxC,sBAAsByC,YAAY,CAAC,UAAU,QAAQ;QACvEN,OAAOK,KAAKE,OAAO,CAACJ;QACpBH,OAAOlC,aAAauB,OAAO,EAAEmB,oBAAoB,CAACN,WAAW,QAAQ;QACrEF,OAAO/D,cAAM,EAAEwE,GAAG,CAACC,gBAAgB,IAAG,mCAAmC;IAC3E;IAEAX,GAAG,sFAAsF;QACvF,0BAA0B;QAC1BjC,aAAasB,QAAQ,CAAC/C,iBAAiB,CAAC;YAAEc,OAAO;YAAQG,SAAS;YAAOC,UAAU;QAAM;QACzF,MAAMoD,kBAAkBnF,KAAK0C,KAAK,CAACL,qBAAqB,CAAC,SAAS,EAAE,SAAS3B,kBAAkB,CAAC,IAAMI;QACtG,MAAM0D,OAAOnC,sBAAsByC,YAAY,CAAC,QAAQ,QAAQM,OAAO,CAACC,OAAO,CAAC;QAChFb,OAAOW,iBAAiBD,gBAAgB;QAExC,4FAA4F;QAC5F,MAAMI,eAAoB9D,UAAU;YAAEE,IAAI;QAAE;QAC5CY,aAAasB,QAAQ,CAAC/C,iBAAiB,CAACyE;QACxC3E,gBAAgBC;QAChBO,kBAAkBP,YAAY;YAAC;gBAAE2E,KAAK;gBAAQC,IAAI;gBAAQC,MAAM;YAAmB;SAAE;QACrF,MAAMjB,OAAOnC,sBAAsByC,YAAY,CAAC,QAAQ,QAAQM,OAAO,CAACC,OAAO,CAAC;IAClF;IAEAd,GAAG,8EAA8E;QAC/E,mGAAmG;QACnG,MAAMe,eAAoB9D,UAAU;YAAEE,IAAI;QAAE;QAC5CY,aAAasB,QAAQ,CAAC/C,iBAAiB,CAACyE;QACxC,+CAA+C;QAC/CvE,sBAAsBH,YAAY8E,+BAAuB,EAAE;QAC3D,mEAAmE;QACnE,MAAMC,iBAAiB3F,KAAK0C,KAAK,CAACL,qBAAqB,CAAC,SAAS,EAAE,SAAS3B,kBAAkB,CAAC,IAAMI;QACrGwB,aAAawB,cAAc,CAAC8B,qBAAqB,CAAC,IAAIC,MAAM;QAC5D,MAAMC,OAAO,MAAMzD,sBAAsByC,YAAY,CAAC,QAAQ,UAAU;QACxEN,OAAOsB,MAAMC,QAAQ;QACrBvB,OAAOlC,aAAawB,cAAc,EAAEkB,oBAAoB,CAACM,cAAc,YAAY;QACnFd,OAAOmB,gBAAgBT,gBAAgB;QAEvC,2CAA2C;QAC3C5C,aAAawB,cAAc,CAACkC,SAAS;QACrC1D,aAAasB,QAAQ,CAAC/C,iBAAiB,CAAC;QACxCD,WAAWP,IAAI,CAACa,iBAAiB,CAAC,IAAIwE,+BAAuB,CAAC;QAC9D9E,WAAWJ,MAAM,CAACK,iBAAiB,CAACC;QACpC,MAAMmF,OAAO,MAAM5D,sBAAsByC,YAAY,CAAC,QAAQ;QAC9DN,OAAOyB,MAAMF,QAAQ;QACrBvB,OAAOlC,aAAawB,cAAc,EAAEmB,GAAG,CAACC,gBAAgB;IAC1D;IAEAX,GAAG,iGAAiG;QAClG,kEAAkE;QAClEjC,aAAasB,QAAQ,CAAC/C,iBAAiB,CAAC;QACxCF,gBAAgBC;QAChB,sCAAsC;QACtCO,kBAAkBP,YAAY;YAAC;gBAAE2E,KAAK;gBAAQC,IAAI;gBAAYC,MAAM3E;YAAU;SAAE;QAChF,MAAM6E,iBAAiB3F,KAAK0C,KAAK,CAACL,qBAAqB,CAAC,SAAS,EAAE,SAAS3B,kBAAkB,CAAC,IAAMI;QACrG,MAAMoF,OAAO,MAAM7D,sBAAsByC,YAAY,CAAC,QAAQ;QAC9DN,OAAO0B,MAAMH,QAAQ;QACrBvB,OAAOjC,kBAAkB2B,iBAAiB,EAAEe,GAAG,CAACC,gBAAgB;QAChEV,OAAOmB,gBAAgBT,gBAAgB;QAEvC,qDAAqD;QACrD,mEAAmE;QACnE,MAAMiB,eAAenG,KAAK0C,KAAK,CAAWL,uBAA8B;QACxE8D,aAAaC,qBAAqB,CAAC;YAAEb,KAAK;YAAQC,IAAI;YAAYC,MAAM;QAAmB;QAC3FlD,kBAAkB2B,iBAAiB,CAAC8B,SAAS;QAC7C1D,aAAasB,QAAQ,CAAC/C,iBAAiB,CAAC;QACxC,MAAMwF,cAAmB;YAAE3E,IAAI;YAAGC,OAAO;YAAQG,SAAS;YAAOC,UAAU;YAAMC,WAAWhC,KAAKM,EAAE;QAAG;QACtGiC,kBAAkB2B,iBAAiB,CAACrD,iBAAiB,CAACwF;QACtD,gEAAgE;QAChE/D,aAAa0B,UAAU,CAACnD,iBAAiB,CAACwF;QAC1C,sCAAsC;QACtC,MAAMC,kBAAkB7D;QACxBH,aAAawB,cAAc,CAAC8B,qBAAqB,CAAC,IAAIC,MAAM;QAC5D,MAAMU,OAAO,MAAMlE,sBAAsByC,YAAY,CAAC,QAAQ,OAAO;QACrEN,OAAOjC,kBAAkB2B,iBAAiB,EAAEc,oBAAoB,CAC9D;YAAErD,OAAO;YAAQC,OAAO;YAAoBC,UAAU;YAAO2E,WAAW;YAAQC,UAAU;QAAM,GAChGjC,OAAOkC,QAAQ,GAAG,iBAAiB;;QAErClC,OAAO+B,MAAMI,IAAI,CAACN;QAClB7B,OAAOlC,aAAawB,cAAc,EAAEkB,oBAAoB,CAACqB,aAAa,gBAAgB;QACtF7B,OAAO8B,iBAAiBpB,gBAAgB;QACxC,6CAA6C;QAC7C3C,kBAAkB2B,iBAAiB,CAAC8B,SAAS;QAC7C1D,aAAasB,QAAQ,CAAC/C,iBAAiB,CAAC;QACxC2B,iBAAiB;YAAC;gBAAE+C,KAAK;gBAASC,IAAI;gBAAcC,MAAM;oBAAC;oBAAqB;iBAAqB;YAAC;SAAE;QACxG,MAAMmB,eAAoB;YAAElF,IAAI;YAAGC,OAAO;YAASK,WAAWhC,KAAKM,EAAE;QAAG;QACxEiC,kBAAkB2B,iBAAiB,CAACrD,iBAAiB,CAAC+F;QACtDtE,aAAa0B,UAAU,CAACnD,iBAAiB,CAAC+F;QAC1C,MAAMC,OAAO,MAAMxE,sBAAsByC,YAAY,CAAC,SAAS;QAC/DN,OAAOjC,kBAAkB2B,iBAAiB,EAAEc,oBAAoB,CAACR,OAAOsC,gBAAgB,CAAC;YAAElF,OAAO;QAAoB,IAAI4C,OAAOkC,QAAQ;QACzIlC,OAAOqC,MAAMF,IAAI,CAACC;IACpB;IAEArC,GAAG,kGAAkG;QACnG,oEAAoE;QACpE,MAAMe,eAAoB9D,UAAU;YAAEE,IAAI;QAAE;QAC5CY,aAAasB,QAAQ,CAAC/C,iBAAiB,CAACyE;QACxC,yDAAyD;QACzD9C,iBAAiB;YAAC;gBAAE+C,KAAK;gBAAQC,IAAI;gBAAYC,MAAM;YAAmB;SAAE;QAC5E,4CAA4C;QAC5ClD,kBAAkB4B,iBAAiB,CAACtD,iBAAiB,CAACC;QACtD,2EAA2E;QAC3E,8EAA8E;QAC9E,MAAMiG,aAAa/G,KAAK0C,KAAK,CAACsE,YAAiB,mBAAmBnG,iBAAiB,CAAC;QACpF,MAAMgE,MAAM,MAAMxC,sBAAsByC,YAAY,CAAC,QAAQ,sBAAsB;QACnFN,OAAOjC,kBAAkB4B,iBAAiB,EAAEa,oBAAoB,CAC9D,GACAR,OAAOsC,gBAAgB,CAAC;YACtBlF,OAAO;YACP4E,WAAW;YACXC,UAAU;QACZ;QAEF,iGAAiG;QACjGjC,OAAOc,aAAazD,QAAQ,EAAE8E,IAAI,CAAC;QACnC,yCAAyC;QACzCnC,OAAOc,aAAa1D,KAAK,EAAE+E,IAAI,CAAC;QAChCnC,OAAOc,cAAc2B,aAAa,CAAC;YAAET,WAAW;YAAQC,UAAU;QAAM;QACxE,8BAA8B;QAC9BjC,OAAOlC,aAAawB,cAAc,EAAEkB,oBAAoB,CAACM,cAAc,aAAa;QACpF,qCAAqC;QACrCd,OAAOK,KAAK8B,IAAI,CAACrB;QAEjB,iGAAiG;QACjG/C,kBAAkB4B,iBAAiB,CAAC6B,SAAS;QAC7C1D,aAAawB,cAAc,CAACkC,SAAS;QACrC,wDAAwD;QACxDV,aAAa1D,KAAK,GAAG;QACrBmF,WAAWlG,iBAAiB,CAAC;QAC7B,MAAMoF,OAAO,MAAM5D,sBAAsByC,YAAY,CAAC,QAAQ,uBAAuB;QACrF,qEAAqE;QACrEN,OAAOjC,kBAAkB4B,iBAAiB,EAAEe,gBAAgB;QAC5D,MAAMgC,aAAa3E,kBAAkB4B,iBAAiB,CAAClE,IAAI,CAACkH,KAAK,CAAC,EAAE;QACpE3C,OAAO0C,UAAU,CAAC,EAAE,EAAEP,IAAI,CAAC;QAC3BnC,OAAO0C,UAAU,CAAC,EAAE,EAAEnC,OAAO,CAC3BP,OAAOsC,gBAAgB,CAAC;YACtBlF,OAAO;QACT;QAEF4C,OAAO0C,UAAU,CAAC,EAAE,EAAEnC,OAAO,CAACP,OAAOS,GAAG,CAAC6B,gBAAgB,CAAC;YAAEjF,UAAU2C,OAAOkC,QAAQ;QAAG;QACxF,qCAAqC;QACrClC,OAAOc,aAAazD,QAAQ,EAAE8E,IAAI,CAAC;QACnC,8BAA8B;QAC9BnC,OAAOlC,aAAawB,cAAc,EAAEkB,oBAAoB,CAACM,cAAc,aAAa;QACpF,qCAAqC;QACrCd,OAAOyB,MAAMU,IAAI,CAACrB;QAClB,sFAAsF;QACtF/C,kBAAkB4B,iBAAiB,CAAC6B,SAAS;QAC7C1D,aAAawB,cAAc,CAACkC,SAAS;QACrCe,WAAWlG,iBAAiB,CAAC;QAC7B,uDAAuD;QACvD,MAAMuG,OAAO,MAAM/E,sBAAsByC,YAAY,CAAC,QAAQ,uBAAuB;QACrF,gCAAgC;QAChCN,OAAOjC,kBAAkB4B,iBAAiB,EAAEc,GAAG,CAACC,gBAAgB;QAChE,4CAA4C;QAC5CV,OAAOlC,aAAawB,cAAc,EAAEkB,oBAAoB,CAACM,cAAc,aAAa;QACpF,qCAAqC;QACrCd,OAAO4C,MAAMT,IAAI,CAACrB;IACpB;IAEAf,GAAG,mFAAmF;QACpF,mEAAmE;QACnE,MAAMe,eAAoB;YAAE5D,IAAI;YAAGC,OAAO;YAASG,SAAS;YAAOC,UAAU;QAAK;QAClFO,aAAasB,QAAQ,CAAC/C,iBAAiB,CAACyE;QACxC9C,iBAAiB,EAAE;QACnB,MAAM0D,OAAO,MAAM7D,sBAAsByC,YAAY,CAAC,SAAS,OAAO;QACtEN,OAAO0B,MAAMH,QAAQ;QACrBvB,OAAOlC,aAAawB,cAAc,EAAEkB,oBAAoB,CAACM,cAAc,aAAa;QAEpF,iEAAiE;QACjEtF,KAAKqH,aAAa;QAClB,MAAMC,gBAAqB;YAAE5F,IAAI;YAAIC,OAAO;YAAQG,SAAS;YAAOC,UAAU;QAAK;QACnFO,aAAasB,QAAQ,CAAC/C,iBAAiB,CAACyG;QACxC3G,gBAAgBC;QAChBU,iBAAiBV,YAAY,IAAIiF,MAAM;QACvC,MAAMU,OAAO,MAAMlE,sBAAsByC,YAAY,CAAC,QAAQ,OAAO;QACrEN,OAAO+B,MAAMR,QAAQ;QACrBvB,OAAOlC,aAAawB,cAAc,EAAEkB,oBAAoB,CAACsC,eAAe,WAAW;IACrF;IAEA/C,GAAG,mEAAmE;QACpE,MAAMe,eAAoB9D,UAAU;YAAEE,IAAI;QAAG;QAC7CY,aAAasB,QAAQ,CAAC/C,iBAAiB,CAACyE;QACxC,2BAA2B;QAC3BvE,sBAAsBH,YAAY8E,+BAAuB,EAAE;QAC3D,uBAAuB;QACvBpD,aAAayB,mBAAmB,CAAClD,iBAAiB,CAAC;QACnD,MAAMgE,MAAM,MAAMxC,sBAAsByC,YAAY,CAAC,QAAQ,gBAAgB,YAAY;QACzFN,OAAOK,KAAK8B,IAAI,CAACrB;QACjBd,OAAOlC,aAAayB,mBAAmB,EAAEiB,oBAAoB,CAACM,cAAc,gBAAgB,YAAY;QACxGd,OAAOlC,aAAawB,cAAc,EAAEkB,oBAAoB,CAACM,cAAc,YAAY;IACrF;IAEAf,GAAG,kEAAkE;QACnE,oDAAoD;QACpDjC,aAAasB,QAAQ,CAAC/C,iBAAiB,CAAC;QACxC,MAAM0G,OAAO,IAAI1B,MAAM;QACvB,MAAM2B,OAAOC,OAAOC,MAAM,CAAC,IAAI7B,MAAM,yBAAyB;YAAE8B,MAAMC,MAAMC,IAAI,CAACC,gCAAkB,CAAC,CAAC,EAAE;QAAC;QACxGlH,WAAWP,IAAI,CAACa,iBAAiB,CAAC;YAAE6G,QAAQ;gBAACR;gBAAMC;aAAK;QAAC;QACzD5G,WAAWJ,MAAM,CAACK,iBAAiB,CAACC;QAEpC,4DAA4D;QAC5D,MAAM0D,OAAOnC,sBAAsByC,YAAY,CAAC,QAAQ,QAAQM,OAAO,CAACC,OAAO,CAAC;QAEhF,iHAAiH;QACjHzE,WAAWP,IAAI,CAAC2H,SAAS;QACzBpH,WAAWJ,MAAM,CAACwH,SAAS;QAC3B1F,aAAawB,cAAc,CAACkC,SAAS;QACrC1D,aAAasB,QAAQ,CAAC/C,iBAAiB,CAAC;QACxCD,WAAWP,IAAI,CAACa,iBAAiB,CAAC,IAAI2E,MAAM;QAC5CjF,WAAWJ,MAAM,CAACK,iBAAiB,CAACC;QAEpC,MAAM+D,MAAM,MAAMxC,sBAAsByC,YAAY,CAAC,QAAQ;QAC7DN,OAAOK,KAAKkB,QAAQ;QACpBvB,OAAOlC,aAAawB,cAAc,EAAEmB,GAAG,CAACC,gBAAgB;IAC1D;IAEAX,GAAG,yDAAyD;QAC1D,+CAA+C;QAC/C,MAAMe,eAAoB9D,UAAU;YAAEE,IAAI;YAAIE,OAAO;QAAa;QAClEU,aAAasB,QAAQ,CAAC/C,iBAAiB,CAACyE;QACxC,yFAAyF;QACzF9C,iBAAiB;YAAC;gBAAE+C,KAAK;gBAAQC,IAAI;gBAAYC,MAAM;YAAmB;SAAE;QAC5ElD,kBAAkB4B,iBAAiB,CAACjD,iBAAiB,CAAC,IAAI2E,MAAM;QAChE,6DAA6D;QAC7D7F,KAAK0C,KAAK,CAACsE,YAAiB,mBAAmBnG,iBAAiB,CAAC;QACjEb,KAAK0C,KAAK,CAACsE,YAAiB,iBAAiBiB,eAAe,CAAC;YAAEzB,WAAW;YAAQC,UAAU;QAAM;QAClG,MAAM5B,MAAM,MAAMxC,sBAAsByC,YAAY,CAAC,QAAQ;QAC7DN,OAAOjC,kBAAkB4B,iBAAiB,EAAEe,gBAAgB;QAC5D,6CAA6C;QAC7CV,OAAOc,aAAa1D,KAAK,EAAE+E,IAAI,CAAC;QAChCnC,OAAOK,KAAK8B,IAAI,CAACrB;IACnB;IAEAf,GAAG,uGAAuG;QACxG,iIAAiI;QACjI,MAAM2D,QAAa;YAAExG,IAAI;YAAIC,OAAO;YAAQG,SAAS;YAAOC,UAAU;QAAK;QAC3EO,aAAasB,QAAQ,CAAC/C,iBAAiB,CAACqH;QACxCtH,WAAWP,IAAI,CAACQ,iBAAiB,CAACC;QAElC,qHAAqH;QACrHd,KAAKqH,aAAa;QAClB,MAAMc,QAAa3G,UAAU;YAAEE,IAAI;YAAIE,OAAO;QAAa;QAC3DU,aAAasB,QAAQ,CAAC/C,iBAAiB,CAACsH;QACxC3F,iBAAiB;YAAC;gBAAE+C,KAAK;gBAAQC,IAAI;gBAAYC,MAAM;YAAmB;SAAE;QAC5ElD,kBAAkB4B,iBAAiB,CAACtD,iBAAiB,CAACC;QAEtD,qFAAqF;QACrFd,KAAK0C,KAAK,CAACsE,YAAiB,mBAAmBnG,iBAAiB,CAAC;QACjEb,KAAK0C,KAAK,CAACsE,YAAiB,iBAAiBiB,eAAe,CAAC;YAAEzB,WAAW;YAAQC,UAAU;QAAM;QAClG,MAAMF,OAAO,MAAMlE,sBAAsByC,YAAY,CAAC,QAAQ,UAAU;QAExE,mCAAmC;QACnCN,OAAOjC,kBAAkB4B,iBAAiB,EAAEa,oBAAoB,CAC9D,IACAR,OAAOsC,gBAAgB,CAAC;YAAElF,OAAO;YAAoB4E,WAAW;YAAQC,UAAU;QAAM;QAG1F,2EAA2E;QAC3EjC,OAAO2D,MAAMtG,QAAQ,EAAE8E,IAAI,CAAC;QAC5BnC,OAAO2D,MAAMvG,KAAK,EAAE+E,IAAI,CAAC;QACzBnC,OAAO2D,OAAOlB,aAAa,CAAC;YAAET,WAAW;YAAQC,UAAU;QAAM;QACjEjC,OAAO+B,MAAMI,IAAI,CAACwB;IACpB;AACF"}
|