@hed-hog/core 0.0.390 → 0.0.393
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/README.md +39 -0
- package/dist/access-log/access-log.controller.d.ts.map +1 -1
- package/dist/access-log/access-log.controller.js +1 -1
- package/dist/access-log/access-log.controller.js.map +1 -1
- package/dist/access-log/access-log.interceptor.d.ts.map +1 -1
- package/dist/access-log/access-log.interceptor.js +8 -5
- package/dist/access-log/access-log.interceptor.js.map +1 -1
- package/dist/access-log/access-log.service.d.ts +4 -0
- package/dist/access-log/access-log.service.d.ts.map +1 -1
- package/dist/access-log/access-log.service.js +31 -4
- package/dist/access-log/access-log.service.js.map +1 -1
- package/dist/access-log/dto/query-access-log.dto.d.ts +2 -0
- package/dist/access-log/dto/query-access-log.dto.d.ts.map +1 -1
- package/dist/access-log/dto/query-access-log.dto.js +5 -0
- package/dist/access-log/dto/query-access-log.dto.js.map +1 -1
- package/dist/ai/ai.controller.d.ts +12 -8
- package/dist/ai/ai.controller.d.ts.map +1 -1
- package/dist/ai/ai.controller.js +17 -2
- package/dist/ai/ai.controller.js.map +1 -1
- package/dist/ai/ai.module.d.ts.map +1 -1
- package/dist/ai/ai.module.js +3 -2
- package/dist/ai/ai.module.js.map +1 -1
- package/dist/ai/ai.service.d.ts +30 -1
- package/dist/ai/ai.service.d.ts.map +1 -1
- package/dist/ai/ai.service.js +97 -3
- package/dist/ai/ai.service.js.map +1 -1
- package/dist/ai/dto/richtext-edit-html.dto.d.ts +5 -0
- package/dist/ai/dto/richtext-edit-html.dto.d.ts.map +1 -0
- package/dist/ai/dto/richtext-edit-html.dto.js +27 -0
- package/dist/ai/dto/richtext-edit-html.dto.js.map +1 -0
- package/dist/ai/richtext-ai.prompts.d.ts +5 -0
- package/dist/ai/richtext-ai.prompts.d.ts.map +1 -0
- package/dist/ai/richtext-ai.prompts.js +57 -0
- package/dist/ai/richtext-ai.prompts.js.map +1 -0
- package/dist/ai/richtext-ai.service.d.ts +34 -0
- package/dist/ai/richtext-ai.service.d.ts.map +1 -0
- package/dist/ai/richtext-ai.service.js +95 -0
- package/dist/ai/richtext-ai.service.js.map +1 -0
- package/dist/ai-instruction/ai-instruction.controller.d.ts +5 -5
- package/dist/auth/auth.controller.d.ts +66 -7
- package/dist/auth/auth.controller.d.ts.map +1 -1
- package/dist/auth/auth.controller.js +67 -23
- package/dist/auth/auth.controller.js.map +1 -1
- package/dist/auth/auth.module.d.ts.map +1 -1
- package/dist/auth/auth.module.js +2 -0
- package/dist/auth/auth.module.js.map +1 -1
- package/dist/auth/auth.service.d.ts +136 -4
- package/dist/auth/auth.service.d.ts.map +1 -1
- package/dist/auth/auth.service.js +246 -48
- package/dist/auth/auth.service.js.map +1 -1
- package/dist/auth/dto/confirm-email.dto.d.ts +4 -0
- package/dist/auth/dto/confirm-email.dto.d.ts.map +1 -0
- package/dist/auth/dto/confirm-email.dto.js +22 -0
- package/dist/auth/dto/confirm-email.dto.js.map +1 -0
- package/dist/auth/dto/resend-confirmation.dto.d.ts +8 -0
- package/dist/auth/dto/resend-confirmation.dto.d.ts.map +1 -0
- package/dist/auth/dto/resend-confirmation.dto.js +36 -0
- package/dist/auth/dto/resend-confirmation.dto.js.map +1 -0
- package/dist/authorize/authorize.controller.d.ts +1 -1
- package/dist/authorize/authorize.controller.d.ts.map +1 -1
- package/dist/authorize/authorize.controller.js +4 -2
- package/dist/authorize/authorize.controller.js.map +1 -1
- package/dist/authorize/authorize.service.d.ts +7 -0
- package/dist/authorize/authorize.service.d.ts.map +1 -1
- package/dist/authorize/authorize.service.js +1 -1
- package/dist/authorize/authorize.service.js.map +1 -1
- package/dist/challenge/challenge.service.d.ts +18 -5
- package/dist/challenge/challenge.service.d.ts.map +1 -1
- package/dist/challenge/challenge.service.js +50 -3
- package/dist/challenge/challenge.service.js.map +1 -1
- package/dist/core.module.d.ts.map +1 -1
- package/dist/core.module.js +17 -0
- package/dist/core.module.js.map +1 -1
- package/dist/dashboard/dashboard/dashboard.controller.d.ts +6 -6
- package/dist/dashboard/dashboard/dashboard.service.d.ts +6 -6
- package/dist/dashboard/dashboard-component/dashboard-component.controller.d.ts +4 -4
- package/dist/dashboard/dashboard-component/dashboard-component.service.d.ts +4 -4
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.controller.d.ts +1 -1
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.d.ts +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts +2 -2
- package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts +2 -2
- package/dist/dashboard/dashboard-item/dashboard-item.controller.d.ts +2 -2
- package/dist/dashboard/dashboard-item/dashboard-item.service.d.ts +2 -2
- package/dist/file/file.controller.d.ts +8 -1
- package/dist/file/file.controller.d.ts.map +1 -1
- package/dist/file/file.controller.js +45 -7
- package/dist/file/file.controller.js.map +1 -1
- package/dist/file/file.service.d.ts +128 -1
- package/dist/file/file.service.d.ts.map +1 -1
- package/dist/file/file.service.js +319 -8
- package/dist/file/file.service.js.map +1 -1
- package/dist/file/image-optimization.service.d.ts +43 -0
- package/dist/file/image-optimization.service.d.ts.map +1 -1
- package/dist/file/image-optimization.service.js +154 -0
- package/dist/file/image-optimization.service.js.map +1 -1
- package/dist/file/provider/abstract.provider.d.ts +16 -0
- package/dist/file/provider/abstract.provider.d.ts.map +1 -1
- package/dist/file/provider/abstract.provider.js +4 -0
- package/dist/file/provider/abstract.provider.js.map +1 -1
- package/dist/file/provider/gcs.provider.d.ts +2 -0
- package/dist/file/provider/gcs.provider.d.ts.map +1 -1
- package/dist/file/provider/gcs.provider.js +10 -0
- package/dist/file/provider/gcs.provider.js.map +1 -1
- package/dist/file/provider/local.provider.d.ts +1 -0
- package/dist/file/provider/local.provider.d.ts.map +1 -1
- package/dist/file/provider/local.provider.js +13 -0
- package/dist/file/provider/local.provider.js.map +1 -1
- package/dist/file/provider/s3.provider.d.ts +5 -0
- package/dist/file/provider/s3.provider.d.ts.map +1 -1
- package/dist/file/provider/s3.provider.js +15 -0
- package/dist/file/provider/s3.provider.js.map +1 -1
- package/dist/file/provider/spaces.provider.d.ts +5 -0
- package/dist/file/provider/spaces.provider.d.ts.map +1 -1
- package/dist/file/provider/spaces.provider.js +16 -0
- package/dist/file/provider/spaces.provider.js.map +1 -1
- package/dist/impersonation/dto/redeem-impersonation.dto.d.ts +4 -0
- package/dist/impersonation/dto/redeem-impersonation.dto.d.ts.map +1 -0
- package/dist/impersonation/dto/redeem-impersonation.dto.js +23 -0
- package/dist/impersonation/dto/redeem-impersonation.dto.js.map +1 -0
- package/dist/impersonation/dto/start-impersonation.dto.d.ts +7 -0
- package/dist/impersonation/dto/start-impersonation.dto.d.ts.map +1 -0
- package/dist/impersonation/dto/start-impersonation.dto.js +27 -0
- package/dist/impersonation/dto/start-impersonation.dto.js.map +1 -0
- package/dist/impersonation/impersonation-restrictions.d.ts +26 -0
- package/dist/impersonation/impersonation-restrictions.d.ts.map +1 -0
- package/dist/impersonation/impersonation-restrictions.js +76 -0
- package/dist/impersonation/impersonation-restrictions.js.map +1 -0
- package/dist/impersonation/impersonation.controller.d.ts +90 -0
- package/dist/impersonation/impersonation.controller.d.ts.map +1 -0
- package/dist/impersonation/impersonation.controller.js +135 -0
- package/dist/impersonation/impersonation.controller.js.map +1 -0
- package/dist/impersonation/impersonation.module.d.ts +8 -0
- package/dist/impersonation/impersonation.module.d.ts.map +1 -0
- package/dist/impersonation/impersonation.module.js +44 -0
- package/dist/impersonation/impersonation.module.js.map +1 -0
- package/dist/impersonation/impersonation.service.d.ts +164 -0
- package/dist/impersonation/impersonation.service.d.ts.map +1 -0
- package/dist/impersonation/impersonation.service.js +408 -0
- package/dist/impersonation/impersonation.service.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -1
- package/dist/integration/services/outbox-polling.coordinator.d.ts +2 -0
- package/dist/integration/services/outbox-polling.coordinator.d.ts.map +1 -1
- package/dist/integration/services/outbox-polling.coordinator.js +50 -30
- package/dist/integration/services/outbox-polling.coordinator.js.map +1 -1
- package/dist/integration/services/outbox.service.d.ts.map +1 -1
- package/dist/integration/services/outbox.service.js +5 -1
- package/dist/integration/services/outbox.service.js.map +1 -1
- package/dist/integration-profile/integration-profile.secrets.d.ts.map +1 -1
- package/dist/integration-profile/integration-profile.secrets.js +3 -0
- package/dist/integration-profile/integration-profile.secrets.js.map +1 -1
- package/dist/integration-profile/integration-profile.utils.d.ts +14 -0
- package/dist/integration-profile/integration-profile.utils.d.ts.map +1 -1
- package/dist/integration-profile/integration-profile.utils.js +26 -0
- package/dist/integration-profile/integration-profile.utils.js.map +1 -1
- package/dist/integration-profile/testers/email.testers.d.ts.map +1 -1
- package/dist/integration-profile/testers/email.testers.js +28 -0
- package/dist/integration-profile/testers/email.testers.js.map +1 -1
- package/dist/language/en.json +11 -0
- package/dist/language/pt.json +11 -0
- package/dist/mail/dto/send.dto.d.ts +38 -1
- package/dist/mail/dto/send.dto.d.ts.map +1 -1
- package/dist/mail/dto/send.dto.js +10 -0
- package/dist/mail/dto/send.dto.js.map +1 -1
- package/dist/mail/mail.controller.d.ts +1 -0
- package/dist/mail/mail.controller.d.ts.map +1 -1
- package/dist/mail/mail.module.d.ts.map +1 -1
- package/dist/mail/mail.module.js +2 -0
- package/dist/mail/mail.module.js.map +1 -1
- package/dist/mail/mail.service.d.ts +48 -4
- package/dist/mail/mail.service.d.ts.map +1 -1
- package/dist/mail/mail.service.js +187 -35
- package/dist/mail/mail.service.js.map +1 -1
- package/dist/mail-delivery/mail-delivery-provision.service.d.ts +21 -0
- package/dist/mail-delivery/mail-delivery-provision.service.d.ts.map +1 -0
- package/dist/mail-delivery/mail-delivery-provision.service.js +64 -0
- package/dist/mail-delivery/mail-delivery-provision.service.js.map +1 -0
- package/dist/mail-delivery/mail-delivery.constants.d.ts +17 -0
- package/dist/mail-delivery/mail-delivery.constants.d.ts.map +1 -0
- package/dist/mail-delivery/mail-delivery.constants.js +31 -0
- package/dist/mail-delivery/mail-delivery.constants.js.map +1 -0
- package/dist/mail-delivery/mail-delivery.controller.d.ts +19 -0
- package/dist/mail-delivery/mail-delivery.controller.d.ts.map +1 -0
- package/dist/mail-delivery/mail-delivery.controller.js +53 -0
- package/dist/mail-delivery/mail-delivery.controller.js.map +1 -0
- package/dist/mail-delivery/mail-delivery.module.d.ts +7 -0
- package/dist/mail-delivery/mail-delivery.module.d.ts.map +1 -0
- package/dist/mail-delivery/mail-delivery.module.js +36 -0
- package/dist/mail-delivery/mail-delivery.module.js.map +1 -0
- package/dist/mail-delivery/mail-delivery.service.d.ts +38 -0
- package/dist/mail-delivery/mail-delivery.service.d.ts.map +1 -0
- package/dist/mail-delivery/mail-delivery.service.js +140 -0
- package/dist/mail-delivery/mail-delivery.service.js.map +1 -0
- package/dist/mail-delivery/ses-event.parser.d.ts +27 -0
- package/dist/mail-delivery/ses-event.parser.d.ts.map +1 -0
- package/dist/mail-delivery/ses-event.parser.js +131 -0
- package/dist/mail-delivery/ses-event.parser.js.map +1 -0
- package/dist/mail-delivery/ses-webhook.service.d.ts +58 -0
- package/dist/mail-delivery/ses-webhook.service.d.ts.map +1 -0
- package/dist/mail-delivery/ses-webhook.service.js +214 -0
- package/dist/mail-delivery/ses-webhook.service.js.map +1 -0
- package/dist/mail-delivery/sns-message.util.d.ts +38 -0
- package/dist/mail-delivery/sns-message.util.d.ts.map +1 -0
- package/dist/mail-delivery/sns-message.util.js +109 -0
- package/dist/mail-delivery/sns-message.util.js.map +1 -0
- package/dist/mail-sent/dto/query-mail-sent-list.dto.d.ts +5 -0
- package/dist/mail-sent/dto/query-mail-sent-list.dto.d.ts.map +1 -1
- package/dist/mail-sent/dto/query-mail-sent-list.dto.js +6 -0
- package/dist/mail-sent/dto/query-mail-sent-list.dto.js.map +1 -1
- package/dist/mail-sent/mail-sent.controller.d.ts +39 -0
- package/dist/mail-sent/mail-sent.controller.d.ts.map +1 -1
- package/dist/mail-sent/mail-sent.service.d.ts +39 -0
- package/dist/mail-sent/mail-sent.service.d.ts.map +1 -1
- package/dist/mail-sent/mail-sent.service.js +45 -4
- package/dist/mail-sent/mail-sent.service.js.map +1 -1
- package/dist/mailbox/mailbox-fetcher.registry.d.ts +100 -0
- package/dist/mailbox/mailbox-fetcher.registry.d.ts.map +1 -0
- package/dist/mailbox/mailbox-fetcher.registry.js +59 -0
- package/dist/mailbox/mailbox-fetcher.registry.js.map +1 -0
- package/dist/mailbox/mailbox.module.d.ts +8 -0
- package/dist/mailbox/mailbox.module.d.ts.map +1 -0
- package/dist/mailbox/mailbox.module.js +27 -0
- package/dist/mailbox/mailbox.module.js.map +1 -0
- package/dist/meeting/meeting-provider.registry.d.ts +155 -0
- package/dist/meeting/meeting-provider.registry.d.ts.map +1 -0
- package/dist/meeting/meeting-provider.registry.js +53 -0
- package/dist/meeting/meeting-provider.registry.js.map +1 -0
- package/dist/meeting/meeting.constants.d.ts +47 -0
- package/dist/meeting/meeting.constants.d.ts.map +1 -0
- package/dist/meeting/meeting.constants.js +21 -0
- package/dist/meeting/meeting.constants.js.map +1 -0
- package/dist/meeting/meeting.module.d.ts +7 -0
- package/dist/meeting/meeting.module.d.ts.map +1 -0
- package/dist/meeting/meeting.module.js +26 -0
- package/dist/meeting/meeting.module.js.map +1 -0
- package/dist/middlewares/locale-injection.middleware.d.ts +12 -0
- package/dist/middlewares/locale-injection.middleware.d.ts.map +1 -1
- package/dist/middlewares/locale-injection.middleware.js +20 -4
- package/dist/middlewares/locale-injection.middleware.js.map +1 -1
- package/dist/oauth/providers/google.provider.d.ts +7 -0
- package/dist/oauth/providers/google.provider.d.ts.map +1 -1
- package/dist/oauth/providers/google.provider.js +43 -13
- package/dist/oauth/providers/google.provider.js.map +1 -1
- package/dist/observability/monitored-cron.d.ts +34 -0
- package/dist/observability/monitored-cron.d.ts.map +1 -0
- package/dist/observability/monitored-cron.js +118 -0
- package/dist/observability/monitored-cron.js.map +1 -0
- package/dist/pairing/dto/approve-reverse-pairing.dto.d.ts +12 -0
- package/dist/pairing/dto/approve-reverse-pairing.dto.d.ts.map +1 -0
- package/dist/pairing/dto/approve-reverse-pairing.dto.js +30 -0
- package/dist/pairing/dto/approve-reverse-pairing.dto.js.map +1 -0
- package/dist/pairing/dto/claim-pairing.dto.d.ts +11 -0
- package/dist/pairing/dto/claim-pairing.dto.d.ts.map +1 -0
- package/dist/pairing/dto/claim-pairing.dto.js +35 -0
- package/dist/pairing/dto/claim-pairing.dto.js.map +1 -0
- package/dist/pairing/dto/confirm-pairing.dto.d.ts +10 -0
- package/dist/pairing/dto/confirm-pairing.dto.d.ts.map +1 -0
- package/dist/pairing/dto/confirm-pairing.dto.js +24 -0
- package/dist/pairing/dto/confirm-pairing.dto.js.map +1 -0
- package/dist/pairing/dto/poll-reverse-pairing.dto.d.ts +5 -0
- package/dist/pairing/dto/poll-reverse-pairing.dto.d.ts.map +1 -0
- package/dist/pairing/dto/poll-reverse-pairing.dto.js +24 -0
- package/dist/pairing/dto/poll-reverse-pairing.dto.js.map +1 -0
- package/dist/pairing/dto/start-pairing.dto.d.ts +11 -0
- package/dist/pairing/dto/start-pairing.dto.d.ts.map +1 -0
- package/dist/pairing/dto/start-pairing.dto.js +30 -0
- package/dist/pairing/dto/start-pairing.dto.js.map +1 -0
- package/dist/pairing/dto/start-reverse-pairing.dto.d.ts +24 -0
- package/dist/pairing/dto/start-reverse-pairing.dto.d.ts.map +1 -0
- package/dist/pairing/dto/start-reverse-pairing.dto.js +47 -0
- package/dist/pairing/dto/start-reverse-pairing.dto.js.map +1 -0
- package/dist/pairing/pairing.controller.d.ts +118 -0
- package/dist/pairing/pairing.controller.d.ts.map +1 -0
- package/dist/pairing/pairing.controller.js +296 -0
- package/dist/pairing/pairing.controller.js.map +1 -0
- package/dist/pairing/pairing.module.d.ts +10 -0
- package/dist/pairing/pairing.module.d.ts.map +1 -0
- package/dist/pairing/pairing.module.js +40 -0
- package/dist/pairing/pairing.module.js.map +1 -0
- package/dist/pairing/pairing.service.d.ts +238 -0
- package/dist/pairing/pairing.service.d.ts.map +1 -0
- package/dist/pairing/pairing.service.js +495 -0
- package/dist/pairing/pairing.service.js.map +1 -0
- package/dist/pdf/pdf.service.d.ts +3 -6
- package/dist/pdf/pdf.service.d.ts.map +1 -1
- package/dist/pdf/pdf.service.js +8 -20
- package/dist/pdf/pdf.service.js.map +1 -1
- package/dist/profile/profile.controller.d.ts +11 -0
- package/dist/profile/profile.controller.d.ts.map +1 -1
- package/dist/profile/profile.service.d.ts +11 -0
- package/dist/profile/profile.service.d.ts.map +1 -1
- package/dist/profile/profile.service.js +20 -11
- package/dist/profile/profile.service.js.map +1 -1
- package/dist/redis/redis-pubsub.d.ts.map +1 -1
- package/dist/redis/redis-pubsub.js +44 -0
- package/dist/redis/redis-pubsub.js.map +1 -1
- package/dist/role/guards/role.guard.d.ts +5 -0
- package/dist/role/guards/role.guard.d.ts.map +1 -1
- package/dist/role/guards/role.guard.js +41 -39
- package/dist/role/guards/role.guard.js.map +1 -1
- package/dist/session/session.controller.d.ts +2 -0
- package/dist/session/session.controller.d.ts.map +1 -1
- package/dist/session/session.service.d.ts +39 -0
- package/dist/session/session.service.d.ts.map +1 -1
- package/dist/session/session.service.js +91 -8
- package/dist/session/session.service.js.map +1 -1
- package/dist/setting/brand-asset.service.d.ts +48 -0
- package/dist/setting/brand-asset.service.d.ts.map +1 -0
- package/dist/setting/brand-asset.service.js +130 -0
- package/dist/setting/brand-asset.service.js.map +1 -0
- package/dist/setting/setting.module.d.ts.map +1 -1
- package/dist/setting/setting.module.js +3 -2
- package/dist/setting/setting.module.js.map +1 -1
- package/dist/setting/setting.service.d.ts +33 -0
- package/dist/setting/setting.service.d.ts.map +1 -1
- package/dist/setting/setting.service.js +88 -0
- package/dist/setting/setting.service.js.map +1 -1
- package/dist/token/token.service.d.ts +23 -2
- package/dist/token/token.service.d.ts.map +1 -1
- package/dist/token/token.service.js +39 -4
- package/dist/token/token.service.js.map +1 -1
- package/dist/user/user.controller.d.ts +8 -22
- package/dist/user/user.controller.d.ts.map +1 -1
- package/dist/user/user.service.d.ts +44 -24
- package/dist/user/user.service.d.ts.map +1 -1
- package/dist/user/user.service.js +89 -6
- package/dist/user/user.service.js.map +1 -1
- package/dist/user-device/user-device.controller.d.ts +54 -0
- package/dist/user-device/user-device.controller.d.ts.map +1 -0
- package/dist/user-device/user-device.controller.js +103 -0
- package/dist/user-device/user-device.controller.js.map +1 -0
- package/dist/user-device/user-device.module.d.ts +3 -0
- package/dist/user-device/user-device.module.d.ts.map +1 -0
- package/dist/user-device/user-device.module.js +26 -0
- package/dist/user-device/user-device.module.js.map +1 -0
- package/dist/user-device/user-device.service.d.ts +71 -0
- package/dist/user-device/user-device.service.d.ts.map +1 -0
- package/dist/user-device/user-device.service.js +158 -0
- package/dist/user-device/user-device.service.js.map +1 -0
- package/dist/utils/device-key.d.ts +23 -0
- package/dist/utils/device-key.d.ts.map +1 -0
- package/dist/utils/device-key.js +31 -0
- package/dist/utils/device-key.js.map +1 -0
- package/dist/utils/geo-ip.d.ts +38 -0
- package/dist/utils/geo-ip.d.ts.map +1 -0
- package/dist/utils/geo-ip.js +122 -0
- package/dist/utils/geo-ip.js.map +1 -0
- package/dist/utils/locale-context.d.ts +10 -7
- package/dist/utils/locale-context.d.ts.map +1 -1
- package/dist/utils/locale-context.js +14 -8
- package/dist/utils/locale-context.js.map +1 -1
- package/dist/utils/security-event-vars.d.ts +56 -0
- package/dist/utils/security-event-vars.d.ts.map +1 -0
- package/dist/utils/security-event-vars.js +109 -0
- package/dist/utils/security-event-vars.js.map +1 -0
- package/dist/utils/user-agent.d.ts +19 -0
- package/dist/utils/user-agent.d.ts.map +1 -0
- package/dist/utils/user-agent.js +36 -0
- package/dist/utils/user-agent.js.map +1 -0
- package/dist/webhook-integration/webhook-integration.service.d.ts +5 -0
- package/dist/webhook-integration/webhook-integration.service.d.ts.map +1 -1
- package/dist/webhook-integration/webhook-integration.service.js +50 -4
- package/dist/webhook-integration/webhook-integration.service.js.map +1 -1
- package/dist/webhook-integration/webhook-protocol.registry.d.ts +6 -0
- package/dist/webhook-integration/webhook-protocol.registry.d.ts.map +1 -1
- package/dist/webhook-integration/webhook-protocol.registry.js.map +1 -1
- package/hedhog/data/integration_provider.yaml +9 -0
- package/hedhog/data/mail.yaml +198 -62
- package/hedhog/data/mail_layout.yaml +43 -33
- package/hedhog/data/menu.yaml +2 -0
- package/hedhog/data/role.yaml +16 -0
- package/hedhog/data/route.yaml +172 -3
- package/hedhog/data/setting_group.yaml +178 -2
- package/hedhog/data/setting_subgroup.yaml +8 -0
- package/hedhog/frontend/app/_lib/hooks/use-core-access.test.tsx.ejs +125 -0
- package/hedhog/frontend/app/_lib/hooks/use-core-access.ts.ejs +29 -0
- package/hedhog/frontend/app/_lib/hooks/use-impersonation-access.test.ts.ejs +71 -0
- package/hedhog/frontend/app/_lib/hooks/use-impersonation-access.ts.ejs +32 -0
- package/hedhog/frontend/app/access-log/page.tsx.ejs +96 -22
- package/hedhog/frontend/app/account/components/active-sessions.tsx.ejs +17 -1
- package/hedhog/frontend/app/account/components/known-devices.tsx.ejs +89 -0
- package/hedhog/frontend/app/account/components/profile-form.tsx.ejs +2 -1
- package/hedhog/frontend/app/account/components/user-devices-list.tsx.ejs +186 -0
- package/hedhog/frontend/app/account/devices/page.tsx.ejs +5 -0
- package/hedhog/frontend/app/account/layout.tsx.ejs +2 -0
- package/hedhog/frontend/app/configurations/[slug]/components/setting-field.test.tsx.ejs +298 -0
- package/hedhog/frontend/app/configurations/[slug]/components/setting-field.tsx.ejs +376 -1236
- package/hedhog/frontend/app/mail/log/page.tsx.ejs +210 -3
- package/hedhog/frontend/app/mail/template/page.tsx.ejs +190 -132
- package/hedhog/frontend/app/users/active-session.tsx.ejs +58 -11
- package/hedhog/frontend/app/users/page.tsx.ejs +69 -10
- package/hedhog/frontend/app/users/simulate-access-dialog.test.tsx.ejs +202 -0
- package/hedhog/frontend/app/users/simulate-access-dialog.tsx.ejs +170 -0
- package/hedhog/frontend/app/users/user-activity-timeline.tsx.ejs +19 -0
- package/hedhog/frontend/app/users/user-devices.tsx.ejs +89 -0
- package/hedhog/frontend/app/users/user-logs.tsx.ejs +13 -1
- package/hedhog/frontend/app/users/user-sheet.tsx.ejs +46 -24
- package/hedhog/frontend/messages/en.json +146 -48
- package/hedhog/frontend/messages/pt.json +146 -48
- package/hedhog/table/access_log.yaml +7 -0
- package/hedhog/table/file.yaml +8 -0
- package/hedhog/table/mail_delivery_event.yaml +76 -0
- package/hedhog/table/mail_sent.yaml +23 -0
- package/hedhog/table/user_activity.yaml +9 -0
- package/hedhog/table/user_device.yaml +52 -0
- package/hedhog/table/user_session.yaml +20 -1
- package/package.json +6 -5
- package/src/access-log/access-log.controller.ts +1 -0
- package/src/access-log/access-log.interceptor.ts +5 -1
- package/src/access-log/access-log.service.spec.ts +33 -2
- package/src/access-log/access-log.service.ts +38 -0
- package/src/access-log/dto/query-access-log.dto.ts +5 -0
- package/src/ai/ai.controller.ts +9 -0
- package/src/ai/ai.module.ts +3 -2
- package/src/ai/ai.service.ts +150 -13
- package/src/ai/dto/richtext-edit-html.dto.ts +11 -0
- package/src/ai/richtext-ai.prompts.ts +57 -0
- package/src/ai/richtext-ai.service.ts +109 -0
- package/src/auth/auth.controller.ts +47 -8
- package/src/auth/auth.module.ts +2 -0
- package/src/auth/auth.service.spec.ts +383 -6
- package/src/auth/auth.service.ts +345 -56
- package/src/auth/dto/confirm-email.dto.ts +7 -0
- package/src/auth/dto/resend-confirmation.dto.ts +20 -0
- package/src/authorize/authorize.controller.ts +2 -0
- package/src/authorize/authorize.service.spec.ts +23 -0
- package/src/authorize/authorize.service.ts +8 -0
- package/src/challenge/challenge.service.spec.ts +73 -0
- package/src/challenge/challenge.service.ts +62 -3
- package/src/core.module.ts +19 -0
- package/src/file/file.controller.ts +54 -4
- package/src/file/file.service.spec.ts +275 -105
- package/src/file/file.service.ts +409 -14
- package/src/file/image-optimization.service.spec.ts +92 -0
- package/src/file/image-optimization.service.ts +202 -0
- package/src/file/provider/abstract.provider.ts +23 -0
- package/src/file/provider/gcs.provider.ts +17 -0
- package/src/file/provider/local.provider.ts +21 -0
- package/src/file/provider/s3.provider.ts +25 -0
- package/src/file/provider/spaces.provider.ts +26 -0
- package/src/impersonation/dto/redeem-impersonation.dto.ts +8 -0
- package/src/impersonation/dto/start-impersonation.dto.ts +13 -0
- package/src/impersonation/impersonation-restrictions.ts +84 -0
- package/src/impersonation/impersonation.controller.ts +109 -0
- package/src/impersonation/impersonation.module.ts +31 -0
- package/src/impersonation/impersonation.service.spec.ts +427 -0
- package/src/impersonation/impersonation.service.ts +577 -0
- package/src/index.ts +21 -0
- package/src/integration/services/outbox-polling.coordinator.spec.ts +90 -0
- package/src/integration/services/outbox-polling.coordinator.ts +62 -40
- package/src/integration/services/outbox.service.spec.ts +8 -0
- package/src/integration/services/outbox.service.ts +19 -13
- package/src/integration-profile/integration-profile.secrets.ts +3 -0
- package/src/integration-profile/integration-profile.utils.ts +38 -0
- package/src/integration-profile/testers/email.testers.ts +34 -1
- package/src/language/en.json +11 -0
- package/src/language/pt.json +11 -0
- package/src/mail/dto/send.dto.ts +46 -2
- package/src/mail/mail-template.spec.ts +79 -2
- package/src/mail/mail.module.ts +2 -0
- package/src/mail/mail.service.spec.ts +307 -6
- package/src/mail/mail.service.ts +227 -30
- package/src/mail-delivery/mail-delivery-provision.service.ts +66 -0
- package/src/mail-delivery/mail-delivery.constants.ts +31 -0
- package/src/mail-delivery/mail-delivery.controller.ts +38 -0
- package/src/mail-delivery/mail-delivery.module.ts +23 -0
- package/src/mail-delivery/mail-delivery.service.spec.ts +136 -0
- package/src/mail-delivery/mail-delivery.service.ts +152 -0
- package/src/mail-delivery/ses-event.parser.spec.ts +140 -0
- package/src/mail-delivery/ses-event.parser.ts +191 -0
- package/src/mail-delivery/ses-webhook.service.spec.ts +170 -0
- package/src/mail-delivery/ses-webhook.service.ts +240 -0
- package/src/mail-delivery/sns-message.util.spec.ts +163 -0
- package/src/mail-delivery/sns-message.util.ts +125 -0
- package/src/mail-sent/dto/query-mail-sent-list.dto.ts +15 -0
- package/src/mail-sent/mail-sent.service.ts +46 -2
- package/src/mailbox/mailbox-fetcher.registry.ts +132 -0
- package/src/mailbox/mailbox.module.ts +14 -0
- package/src/meeting/meeting-provider.registry.ts +193 -0
- package/src/meeting/meeting.constants.ts +53 -0
- package/src/meeting/meeting.module.ts +13 -0
- package/src/middlewares/locale-injection.middleware.ts +25 -6
- package/src/oauth/providers/google.provider.ts +49 -15
- package/src/observability/monitored-cron.spec.ts +102 -0
- package/src/observability/monitored-cron.ts +110 -0
- package/src/pairing/dto/approve-reverse-pairing.dto.ts +20 -0
- package/src/pairing/dto/claim-pairing.dto.ts +19 -0
- package/src/pairing/dto/confirm-pairing.dto.ts +14 -0
- package/src/pairing/dto/poll-reverse-pairing.dto.ts +9 -0
- package/src/pairing/dto/start-pairing.dto.ts +19 -0
- package/src/pairing/dto/start-reverse-pairing.dto.ts +45 -0
- package/src/pairing/pairing.controller.ts +246 -0
- package/src/pairing/pairing.module.ts +27 -0
- package/src/pairing/pairing.service.spec.ts +462 -0
- package/src/pairing/pairing.service.ts +614 -0
- package/src/pdf/pdf.service.ts +6 -24
- package/src/profile/profile.service.ts +26 -8
- package/src/redis/redis-pubsub.ts +11 -0
- package/src/role/guards/role.guard.spec.ts +75 -0
- package/src/role/guards/role.guard.ts +64 -46
- package/src/session/session.service.spec.ts +81 -0
- package/src/session/session.service.ts +93 -6
- package/src/setting/brand-asset.service.ts +137 -0
- package/src/setting/setting.module.ts +3 -2
- package/src/setting/setting.service.spec.ts +79 -1
- package/src/setting/setting.service.ts +109 -1
- package/src/token/token.service.spec.ts +62 -0
- package/src/token/token.service.ts +57 -3
- package/src/user/user.service.spec.ts +33 -2
- package/src/user/user.service.ts +108 -6
- package/src/user-device/user-device.controller.ts +77 -0
- package/src/user-device/user-device.module.ts +13 -0
- package/src/user-device/user-device.service.spec.ts +234 -0
- package/src/user-device/user-device.service.ts +168 -0
- package/src/utils/device-key.spec.ts +57 -0
- package/src/utils/device-key.ts +32 -0
- package/src/utils/geo-ip.spec.ts +134 -0
- package/src/utils/geo-ip.ts +93 -0
- package/src/utils/locale-context.spec.ts +27 -1
- package/src/utils/locale-context.ts +15 -12
- package/src/utils/security-event-vars.spec.ts +161 -0
- package/src/utils/security-event-vars.ts +153 -0
- package/src/utils/user-agent.spec.ts +37 -0
- package/src/utils/user-agent.ts +46 -0
- package/src/webhook-integration/webhook-integration.service.ts +57 -3
- package/src/webhook-integration/webhook-protocol.registry.ts +6 -0
package/src/auth/auth.service.ts
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
getLocaleFromContext,
|
|
3
|
+
getLocaleText,
|
|
4
|
+
isLocaleDetectedFromContext,
|
|
5
|
+
normalizeLocaleCode,
|
|
6
|
+
} from '@hed-hog/api-locale';
|
|
2
7
|
import { PrismaService } from '@hed-hog/api-prisma';
|
|
3
8
|
import { User } from '@hed-hog/api-types';
|
|
4
9
|
import type { PublicKeyCredentialRequestOptionsJSON } from '@simplewebauthn/server';
|
|
5
10
|
import {
|
|
6
11
|
BadRequestException,
|
|
12
|
+
ForbiddenException,
|
|
7
13
|
forwardRef,
|
|
8
14
|
Inject,
|
|
9
15
|
Injectable,
|
|
@@ -17,8 +23,11 @@ import { SecurityService } from '../security/security.service';
|
|
|
17
23
|
import { SessionService } from '../session/session.service';
|
|
18
24
|
import { SettingService } from '../setting/setting.service';
|
|
19
25
|
import { TokenService } from '../token/token.service';
|
|
26
|
+
import { UserDeviceService } from '../user-device/user-device.service';
|
|
20
27
|
import { hasPendingPasswordReset } from '../user/user-credential.util';
|
|
28
|
+
import { CreateWithEmailAndPasswordDTO } from '../user/dto/create-with-email-and-password.dto';
|
|
21
29
|
import { UserService } from '../user/user.service';
|
|
30
|
+
import { buildSecurityEventVars } from '../utils/security-event-vars';
|
|
22
31
|
import { resolveWebAuthnOrigin } from '../utils/webauthn-origin';
|
|
23
32
|
import { ForgetDTO } from './dto/forget.dto';
|
|
24
33
|
import { LoginDTO } from './dto/login.dto';
|
|
@@ -44,6 +53,8 @@ export class AuthService {
|
|
|
44
53
|
private readonly user: UserService,
|
|
45
54
|
@Inject(forwardRef(() => ChallengeService))
|
|
46
55
|
private readonly challenge: ChallengeService,
|
|
56
|
+
@Inject(forwardRef(() => UserDeviceService))
|
|
57
|
+
private readonly userDevice: UserDeviceService,
|
|
47
58
|
) { }
|
|
48
59
|
|
|
49
60
|
async getAuthenticationPayload(locale: string, userId: number, ipAddress: string, userAgent: string, browserId?: string | null) {
|
|
@@ -51,6 +62,18 @@ export class AuthService {
|
|
|
51
62
|
const payload = { sub: userId, sessionId: session.id };
|
|
52
63
|
const accessToken = await this.token.createAccessToken(payload);
|
|
53
64
|
await this.prisma.user.update({ where: { id: userId }, data: { last_login_at: new Date() } });
|
|
65
|
+
|
|
66
|
+
// Unico ponto por onde todo caminho de autenticacao passa (senha, MFA,
|
|
67
|
+
// WebAuthn, recovery code, OAuth, pairing), entao e aqui que o dispositivo
|
|
68
|
+
// e aprendido. Quem checa `isNewDevice` faz isso antes de chegar aqui.
|
|
69
|
+
await this.userDevice.remember(userId, { userAgent, browserId, ipAddress });
|
|
70
|
+
|
|
71
|
+
// O idioma e aprendido junto com o dispositivo, e pelo mesmo motivo: e o
|
|
72
|
+
// unico momento em que o cliente se apresenta. Fica antes do alerta de
|
|
73
|
+
// dispositivo desconhecido (disparado por quem chamou este metodo), entao o
|
|
74
|
+
// proprio e-mail do primeiro login ja sai traduzido.
|
|
75
|
+
await this.persistDetectedLanguage(locale, userId);
|
|
76
|
+
|
|
54
77
|
this.logger.debug(`[AuthService] Session created userId=${userId} sessionId=${session.id}`);
|
|
55
78
|
|
|
56
79
|
return {
|
|
@@ -61,7 +84,66 @@ export class AuthService {
|
|
|
61
84
|
|
|
62
85
|
}
|
|
63
86
|
|
|
64
|
-
|
|
87
|
+
/**
|
|
88
|
+
* Adota o idioma do cliente como preferencia do usuario - uma unica vez.
|
|
89
|
+
*
|
|
90
|
+
* Escrita write-once de proposito: sobrescrever a cada login faria um acesso
|
|
91
|
+
* de aparelho emprestado, ou de um navegador em outro idioma, apagar a
|
|
92
|
+
* escolha que o usuario fez na tela de preferencias.
|
|
93
|
+
*
|
|
94
|
+
* Nunca lanca. Idioma e acabamento; login que falha por causa dele seria uma
|
|
95
|
+
* troca pessima.
|
|
96
|
+
*/
|
|
97
|
+
private async persistDetectedLanguage(locale: string, userId: number) {
|
|
98
|
+
try {
|
|
99
|
+
// `detected` distingue a escolha do cliente do default do middleware.
|
|
100
|
+
// Sem isso, todo cliente que nao manda `Accept-Language` congelaria `en`.
|
|
101
|
+
if (!isLocaleDetectedFromContext()) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const code = normalizeLocaleCode(getLocaleFromContext());
|
|
106
|
+
|
|
107
|
+
if (!code) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// `setSettingUserValue` valida que a setting aceita override, mas nao o
|
|
112
|
+
// valor. Sem esta checagem um navegador em `de-DE` gravaria `de`, e todo
|
|
113
|
+
// e-mail seguinte procuraria um template inexistente.
|
|
114
|
+
const allowed = await this.setting.getSettingListValues('language');
|
|
115
|
+
|
|
116
|
+
if (allowed.length && !allowed.includes(code)) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (await this.setting.getUserSettingValue(userId, 'language')) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
await this.setting.setSettingUserValue(locale, userId, 'language', code);
|
|
125
|
+
} catch (error) {
|
|
126
|
+
this.logger.warn(
|
|
127
|
+
`[AuthService] Could not persist detected language for userId=${userId}: ${error}`,
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async issueDesktopToken(locale: string, userId: number, ipAddress: string, userAgent: string, isImpersonating = false) {
|
|
133
|
+
// Aqui o `userId` vem de @User(), que numa simulacao e o ALVO — sem esta
|
|
134
|
+
// guarda, o operador cunharia para ele uma sessao completa, RENOVAVEL e de
|
|
135
|
+
// 30 dias, furando o teto da simulacao e saindo da trilha de auditoria. O
|
|
136
|
+
// RoleGuard ja bloqueia a rota; isto e a segunda barreira.
|
|
137
|
+
if (isImpersonating) {
|
|
138
|
+
throw new ForbiddenException(
|
|
139
|
+
getLocaleText(
|
|
140
|
+
'impersonation.restrictedRoute',
|
|
141
|
+
locale,
|
|
142
|
+
'This action is not available during an access simulation.',
|
|
143
|
+
),
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
65
147
|
const { accessToken, refreshToken } = await this.getAuthenticationPayload(locale, userId, ipAddress, userAgent);
|
|
66
148
|
return { accessToken, refreshToken };
|
|
67
149
|
}
|
|
@@ -279,7 +361,10 @@ export class AuthService {
|
|
|
279
361
|
user_identifier_id: true,
|
|
280
362
|
user_identifier: {
|
|
281
363
|
select: {
|
|
282
|
-
user_id: true
|
|
364
|
+
user_id: true,
|
|
365
|
+
// Destinatario do alerta de dispositivo novo: o endereco que o
|
|
366
|
+
// usuario de fato verificou para entrar.
|
|
367
|
+
value: true
|
|
283
368
|
}
|
|
284
369
|
}
|
|
285
370
|
}
|
|
@@ -320,14 +405,54 @@ export class AuthService {
|
|
|
320
405
|
|
|
321
406
|
const user = await this.user.findUserById(locale, challenge.user_identifier.user_id);
|
|
322
407
|
|
|
323
|
-
return this.login(locale, user as unknown as User, ipAddress, userAgent, res, browserId, origin);
|
|
408
|
+
return this.login(locale, user as unknown as User, ipAddress, userAgent, res, browserId, origin, challenge.user_identifier.value);
|
|
324
409
|
|
|
325
410
|
} catch (error) {
|
|
326
411
|
throw error;
|
|
327
412
|
}
|
|
328
413
|
}
|
|
329
414
|
|
|
330
|
-
|
|
415
|
+
/**
|
|
416
|
+
* Dispara o alerta de acesso de dispositivo desconhecido.
|
|
417
|
+
*
|
|
418
|
+
* O `await` aqui cobre so a resolucao do app (leitura de setting memoizada);
|
|
419
|
+
* o envio em si continua sem await de proposito, para nao somar a latencia do
|
|
420
|
+
* SMTP ao tempo de resposta do login.
|
|
421
|
+
*/
|
|
422
|
+
private async sendNewDeviceLoginMail(
|
|
423
|
+
locale: string,
|
|
424
|
+
user: { name?: string } | null | undefined,
|
|
425
|
+
email: string | null | undefined,
|
|
426
|
+
ipAddress: string,
|
|
427
|
+
userAgent: string,
|
|
428
|
+
origin?: string,
|
|
429
|
+
): Promise<void> {
|
|
430
|
+
// Tokens de desafio emitidos antes de o `email` passar a ser gravado no
|
|
431
|
+
// payload chegam aqui sem destinatario; alertar ninguem e melhor do que
|
|
432
|
+
// deixar o envio explodir dentro do fluxo de login.
|
|
433
|
+
if (!email) {
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
const app = await this.challenge.resolveAppKeyFromOrigin(origin);
|
|
438
|
+
|
|
439
|
+
// As variaveis sao formatadas aqui (data e nome do pais saem do `Intl`),
|
|
440
|
+
// entao o idioma do destinatario tem que ser resolvido ANTES do envio -
|
|
441
|
+
// senao o corpo sai em portugues com a data em ingles.
|
|
442
|
+
const recipientLocale = await this.mail.resolveRecipientLocale(email, locale);
|
|
443
|
+
|
|
444
|
+
this.mail.sendTemplatedMail(recipientLocale, {
|
|
445
|
+
email,
|
|
446
|
+
slug: 'auth-login-new-device',
|
|
447
|
+
variables: {
|
|
448
|
+
name: user?.name ?? '',
|
|
449
|
+
...buildSecurityEventVars({ ipAddress, userAgent, locale: recipientLocale }),
|
|
450
|
+
},
|
|
451
|
+
app,
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
private async login(locale: string, user: User, ipAddress: string, userAgent: string, res: any, browserId?: string | null, origin?: string, loginEmail?: string) {
|
|
331
456
|
|
|
332
457
|
// Conta excluída pelo próprio usuário: os identificadores já foram
|
|
333
458
|
// anonimizados, mas a checagem fecha qualquer caminho remanescente.
|
|
@@ -352,6 +477,8 @@ export class AuthService {
|
|
|
352
477
|
throw new BadRequestException(getLocaleText('accessDenied', locale, 'Access denied.'));
|
|
353
478
|
}
|
|
354
479
|
|
|
480
|
+
const newDevice = await this.userDevice.isNewDevice(user.id, userAgent, browserId);
|
|
481
|
+
|
|
355
482
|
const { accessToken, refreshToken, session } = await this.getAuthenticationPayload(
|
|
356
483
|
locale,
|
|
357
484
|
user.id,
|
|
@@ -360,26 +487,16 @@ export class AuthService {
|
|
|
360
487
|
browserId,
|
|
361
488
|
);
|
|
362
489
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
490
|
+
if (newDevice) {
|
|
491
|
+
// So o endereco usado para entrar. Avisar todos os identificadores
|
|
492
|
+
// contava, para um e-mail secundario, que o principal tinha sido usado.
|
|
493
|
+
const wanted = (loginEmail ?? '').trim().toLowerCase();
|
|
494
|
+
const recipient =
|
|
495
|
+
emails.find((e) => e.value?.toLowerCase() === wanted)?.value ??
|
|
496
|
+
emails[0].value;
|
|
366
497
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
{
|
|
370
|
-
email: emailObj.value,
|
|
371
|
-
slug: 'auth-login-new-device',
|
|
372
|
-
variables: {
|
|
373
|
-
name: user.name,
|
|
374
|
-
ipAddress,
|
|
375
|
-
userAgent,
|
|
376
|
-
location: 'Unknown',
|
|
377
|
-
},
|
|
378
|
-
app,
|
|
379
|
-
}
|
|
380
|
-
);
|
|
381
|
-
|
|
382
|
-
}
|
|
498
|
+
await this.sendNewDeviceLoginMail(locale, user, recipient, ipAddress, userAgent, origin);
|
|
499
|
+
}
|
|
383
500
|
|
|
384
501
|
await this.user.registerUserActivity(user.id, "login")
|
|
385
502
|
|
|
@@ -466,7 +583,55 @@ export class AuthService {
|
|
|
466
583
|
|
|
467
584
|
}
|
|
468
585
|
|
|
469
|
-
return this.login(locale, user as unknown as User, ipAddress, userAgent, res, browserId, origin);
|
|
586
|
+
return this.login(locale, user as unknown as User, ipAddress, userAgent, res, browserId, origin, email);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* Cadastro por e-mail e senha.
|
|
591
|
+
*
|
|
592
|
+
* Com `require-email-verification` desligada (o default), a conta já nasce
|
|
593
|
+
* utilizável e o e-mail de confirmação é apenas informativo — devolver a
|
|
594
|
+
* pessoa para a tela de login só a obrigaria a digitar de novo o que acabou
|
|
595
|
+
* de digitar, então o cadastro já termina com a sessão emitida.
|
|
596
|
+
*
|
|
597
|
+
* Com a setting ligada, `loginWithEmailAndPassword` barraria o acesso logo em
|
|
598
|
+
* seguida; nesse caso não emitimos sessão e devolvemos `hasChallenge` para o
|
|
599
|
+
* app mandar a pessoa confirmar o endereço antes.
|
|
600
|
+
*/
|
|
601
|
+
async signupWithEmailAndPassword(
|
|
602
|
+
res: any,
|
|
603
|
+
locale: string,
|
|
604
|
+
ipAddress: string,
|
|
605
|
+
userAgent: string,
|
|
606
|
+
data: CreateWithEmailAndPasswordDTO,
|
|
607
|
+
browserId?: string | null,
|
|
608
|
+
origin?: string,
|
|
609
|
+
) {
|
|
610
|
+
const { user } = await this.user.createWithEmailAndPassword(locale, data);
|
|
611
|
+
|
|
612
|
+
const settings = await this.setting.getSettingValues([
|
|
613
|
+
'require-email-verification',
|
|
614
|
+
]);
|
|
615
|
+
|
|
616
|
+
if (settings['require-email-verification'] === true) {
|
|
617
|
+
return { user, hasChallenge: true };
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
// `isNewDevice` retorna false quando o usuário ainda não tem dispositivo
|
|
621
|
+
// nenhum, então o auto-login não dispara o alerta de "novo acesso" — que
|
|
622
|
+
// logo após o cadastro pareceria um aviso de invasão.
|
|
623
|
+
const session = await this.login(
|
|
624
|
+
locale,
|
|
625
|
+
user as unknown as User,
|
|
626
|
+
ipAddress,
|
|
627
|
+
userAgent,
|
|
628
|
+
res,
|
|
629
|
+
browserId,
|
|
630
|
+
origin,
|
|
631
|
+
data.email,
|
|
632
|
+
);
|
|
633
|
+
|
|
634
|
+
return { user, hasChallenge: false, ...session };
|
|
470
635
|
}
|
|
471
636
|
|
|
472
637
|
async verifyRoles(_locale: string, userId: number) {
|
|
@@ -484,13 +649,46 @@ export class AuthService {
|
|
|
484
649
|
})
|
|
485
650
|
}
|
|
486
651
|
|
|
487
|
-
async verifyUser(locale: string, userId: number) {
|
|
652
|
+
async verifyUser(locale: string, userId: number, sessionId?: number | null) {
|
|
488
653
|
const user = await this.user.findUserById(locale, userId);
|
|
489
654
|
const requiresPasswordReset = this.hasPendingPasswordReset(user);
|
|
490
655
|
delete user.user_credential;
|
|
491
656
|
return {
|
|
492
657
|
...user,
|
|
493
658
|
requires_password_reset: requiresPasswordReset,
|
|
659
|
+
// Fonte de verdade do banner de simulacao no cliente: a sessionStorage do
|
|
660
|
+
// navegador e so cache e e gravavel por quem estiver na aba.
|
|
661
|
+
impersonation: await this.describeImpersonation(sessionId),
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
private async describeImpersonation(sessionId?: number | null) {
|
|
666
|
+
if (!sessionId) {
|
|
667
|
+
return null;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
const session = await this.prisma.user_session.findUnique({
|
|
671
|
+
where: { id: sessionId },
|
|
672
|
+
select: {
|
|
673
|
+
impersonator_user_id: true,
|
|
674
|
+
impersonation_reason: true,
|
|
675
|
+
expires_at: true,
|
|
676
|
+
},
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
if (!session?.impersonator_user_id) {
|
|
680
|
+
return null;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
const operator = await this.prisma.user.findUnique({
|
|
684
|
+
where: { id: session.impersonator_user_id },
|
|
685
|
+
select: { id: true, name: true },
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
return {
|
|
689
|
+
by: operator ?? { id: session.impersonator_user_id, name: '' },
|
|
690
|
+
reason: session.impersonation_reason,
|
|
691
|
+
expires_at: session.expires_at.toISOString(),
|
|
494
692
|
};
|
|
495
693
|
}
|
|
496
694
|
|
|
@@ -531,6 +729,17 @@ export class AuthService {
|
|
|
531
729
|
}
|
|
532
730
|
|
|
533
731
|
async logout(res, req, refreshToken: string) {
|
|
732
|
+
// Numa sessao simulada o cookie `rt` da mesma origem e o do OPERADOR: limpar
|
|
733
|
+
// e revogar por hash aqui derrubaria a sessao real dele. O RoleGuard ja
|
|
734
|
+
// bloqueia esta rota durante uma simulacao (impersonation-restrictions.ts);
|
|
735
|
+
// isto e a segunda barreira, no caso de a rota ser alcancada por outro
|
|
736
|
+
// caminho. Sair de uma simulacao e POST /impersonation/stop.
|
|
737
|
+
if (req?.auth?.imp === true) {
|
|
738
|
+
throw new ForbiddenException(
|
|
739
|
+
'This action is not available during an access simulation.',
|
|
740
|
+
);
|
|
741
|
+
}
|
|
742
|
+
|
|
534
743
|
const result = await this.session.deleteRefreshTokenCookie(res, refreshToken);
|
|
535
744
|
const logoutUserId = req.auth?.sub ?? result?.userId ?? null;
|
|
536
745
|
|
|
@@ -628,6 +837,96 @@ export class AuthService {
|
|
|
628
837
|
return { userId: targetUserId, token, hasPassword: passwordCount > 0 };
|
|
629
838
|
}
|
|
630
839
|
|
|
840
|
+
/**
|
|
841
|
+
* Confirma o e-mail a partir do link enviado no cadastro. Não emite sessão: o
|
|
842
|
+
* link só prova o controle da caixa, quem quiser entrar passa pelo login.
|
|
843
|
+
*
|
|
844
|
+
* Idempotente de propósito — clientes de e-mail fazem pré-fetch do link e o
|
|
845
|
+
* usuário costuma clicar de novo depois, e nesses casos ver "link inválido"
|
|
846
|
+
* assusta mais do que ajuda.
|
|
847
|
+
*/
|
|
848
|
+
async confirmEmail(locale: string, token: string) {
|
|
849
|
+
const { userIdentifierId } = await this.token.verifyEmailConfirmationToken(
|
|
850
|
+
locale,
|
|
851
|
+
token,
|
|
852
|
+
);
|
|
853
|
+
|
|
854
|
+
const identifier = await this.prisma.user_identifier.findUnique({
|
|
855
|
+
where: { id: userIdentifierId },
|
|
856
|
+
include: { user: { select: { id: true, name: true } } },
|
|
857
|
+
});
|
|
858
|
+
|
|
859
|
+
if (!identifier) {
|
|
860
|
+
throw new BadRequestException(
|
|
861
|
+
getLocaleText(
|
|
862
|
+
'confirmEmail.invalidOrExpired',
|
|
863
|
+
locale,
|
|
864
|
+
'This confirmation link is invalid or has expired.',
|
|
865
|
+
),
|
|
866
|
+
);
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
if (identifier.verified_at) {
|
|
870
|
+
return {
|
|
871
|
+
confirmed: true,
|
|
872
|
+
alreadyConfirmed: true,
|
|
873
|
+
email: identifier.value,
|
|
874
|
+
name: identifier.user?.name ?? null,
|
|
875
|
+
};
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
await this.prisma.user_identifier.update({
|
|
879
|
+
where: { id: identifier.id },
|
|
880
|
+
data: { verified_at: new Date(), enabled: true },
|
|
881
|
+
});
|
|
882
|
+
|
|
883
|
+
await this.user.registerUserActivity(identifier.user_id, 'confirmEmail');
|
|
884
|
+
|
|
885
|
+
return {
|
|
886
|
+
confirmed: true,
|
|
887
|
+
alreadyConfirmed: false,
|
|
888
|
+
email: identifier.value,
|
|
889
|
+
name: identifier.user?.name ?? null,
|
|
890
|
+
};
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
/**
|
|
894
|
+
* Reenvia o link de confirmação do cadastro.
|
|
895
|
+
*
|
|
896
|
+
* Responde `{ sent: true }` mesmo quando o e-mail não existe ou já está
|
|
897
|
+
* confirmado: qualquer diferença na resposta transformaria a rota num
|
|
898
|
+
* oráculo para descobrir quais endereços têm conta.
|
|
899
|
+
*/
|
|
900
|
+
async resendEmailConfirmation(
|
|
901
|
+
locale: string,
|
|
902
|
+
email: string,
|
|
903
|
+
app?: string,
|
|
904
|
+
origin?: string,
|
|
905
|
+
) {
|
|
906
|
+
try {
|
|
907
|
+
const user = await this.user.findUserByEmail(locale, email);
|
|
908
|
+
|
|
909
|
+
if (user) {
|
|
910
|
+
await this.challenge.sendEmailConfirmationLink(
|
|
911
|
+
locale,
|
|
912
|
+
user.id,
|
|
913
|
+
email,
|
|
914
|
+
app,
|
|
915
|
+
origin,
|
|
916
|
+
);
|
|
917
|
+
}
|
|
918
|
+
} catch (error: any) {
|
|
919
|
+
// `sendEmailConfirmationLink` lança NotFound quando o endereço já está
|
|
920
|
+
// verificado — e uma falha de envio não pode virar erro para o cliente,
|
|
921
|
+
// sob pena de vazar a existência da conta.
|
|
922
|
+
this.logger.warn(
|
|
923
|
+
'Resend of signup confirmation email failed: ' + (error?.message ?? error),
|
|
924
|
+
);
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
return { sent: true };
|
|
928
|
+
}
|
|
929
|
+
|
|
631
930
|
/**
|
|
632
931
|
* Valida (sem consumir) um token de convite de criação de conta e retorna os
|
|
633
932
|
* dados básicos para pré-preencher o formulário no app de treinamentos.
|
|
@@ -851,6 +1150,8 @@ export class AuthService {
|
|
|
851
1150
|
throw new BadRequestException(getLocaleText('invalidMfaCode', locale, 'Invalid MFA code.'));
|
|
852
1151
|
}
|
|
853
1152
|
|
|
1153
|
+
const newDevice = await this.userDevice.isNewDevice(user.id, userAgent, browserId);
|
|
1154
|
+
|
|
854
1155
|
const { accessToken, refreshToken, session } = await this.getAuthenticationPayload(
|
|
855
1156
|
locale,
|
|
856
1157
|
user.id,
|
|
@@ -859,20 +1160,9 @@ export class AuthService {
|
|
|
859
1160
|
browserId,
|
|
860
1161
|
);
|
|
861
1162
|
|
|
862
|
-
|
|
863
|
-
locale,
|
|
864
|
-
|
|
865
|
-
email: payload.email,
|
|
866
|
-
slug: 'auth-login-new-device',
|
|
867
|
-
variables: {
|
|
868
|
-
name: user.name,
|
|
869
|
-
ipAddress,
|
|
870
|
-
userAgent,
|
|
871
|
-
location: 'Unknown',
|
|
872
|
-
},
|
|
873
|
-
app: await this.challenge.resolveAppKeyFromOrigin(origin),
|
|
874
|
-
}
|
|
875
|
-
);
|
|
1163
|
+
if (newDevice) {
|
|
1164
|
+
await this.sendNewDeviceLoginMail(locale, user, payload.email, ipAddress, userAgent, origin);
|
|
1165
|
+
}
|
|
876
1166
|
|
|
877
1167
|
await this.user.registerUserActivity(user.id, "login");
|
|
878
1168
|
const requiresPasswordReset = await this.getRequiresPasswordResetByUserId(
|
|
@@ -881,7 +1171,7 @@ export class AuthService {
|
|
|
881
1171
|
return { accessToken, refreshToken, session, requiresPasswordReset };
|
|
882
1172
|
}
|
|
883
1173
|
|
|
884
|
-
async verifyMfaRecoveryCode(locale: string, mfaToken: string, recoveryCode: string, ipAddress: string, userAgent: string, browserId?: string | null) {
|
|
1174
|
+
async verifyMfaRecoveryCode(locale: string, mfaToken: string, recoveryCode: string, ipAddress: string, userAgent: string, browserId?: string | null, origin?: string) {
|
|
885
1175
|
const payload = await this.token.verifyMfaChallengeToken(mfaToken);
|
|
886
1176
|
const user = await this.prisma.user.findUnique({
|
|
887
1177
|
where: { id: payload.userId },
|
|
@@ -909,6 +1199,8 @@ export class AuthService {
|
|
|
909
1199
|
throw new BadRequestException(getLocaleText('invalidRecoveryCode', locale, 'Invalid recovery code.'));
|
|
910
1200
|
}
|
|
911
1201
|
|
|
1202
|
+
const newDevice = await this.userDevice.isNewDevice(user.id, userAgent, browserId);
|
|
1203
|
+
|
|
912
1204
|
const { accessToken, refreshToken, session } = await this.getAuthenticationPayload(
|
|
913
1205
|
locale,
|
|
914
1206
|
user.id,
|
|
@@ -917,6 +1209,12 @@ export class AuthService {
|
|
|
917
1209
|
browserId,
|
|
918
1210
|
);
|
|
919
1211
|
|
|
1212
|
+
// Entrar por codigo de recuperacao e o caminho mais sensivel dos tres, e era
|
|
1213
|
+
// justamente o unico que nao avisava ninguem.
|
|
1214
|
+
if (newDevice) {
|
|
1215
|
+
await this.sendNewDeviceLoginMail(locale, user, payload.email, ipAddress, userAgent, origin);
|
|
1216
|
+
}
|
|
1217
|
+
|
|
920
1218
|
await this.user.registerUserActivity(user.id, "login");
|
|
921
1219
|
const requiresPasswordReset = await this.getRequiresPasswordResetByUserId(
|
|
922
1220
|
user.id,
|
|
@@ -1058,6 +1356,8 @@ export class AuthService {
|
|
|
1058
1356
|
data: { verified_at: new Date() }
|
|
1059
1357
|
});
|
|
1060
1358
|
|
|
1359
|
+
const newDevice = await this.userDevice.isNewDevice(userId, userAgent, browserId);
|
|
1360
|
+
|
|
1061
1361
|
const { accessToken, refreshToken, session } = await this.getAuthenticationPayload(
|
|
1062
1362
|
locale,
|
|
1063
1363
|
userId,
|
|
@@ -1066,21 +1366,10 @@ export class AuthService {
|
|
|
1066
1366
|
browserId,
|
|
1067
1367
|
);
|
|
1068
1368
|
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
locale,
|
|
1072
|
-
|
|
1073
|
-
email: payload.email,
|
|
1074
|
-
slug: 'auth-login-new-device',
|
|
1075
|
-
variables: {
|
|
1076
|
-
name: user.name,
|
|
1077
|
-
ipAddress,
|
|
1078
|
-
userAgent,
|
|
1079
|
-
location: 'Unknown',
|
|
1080
|
-
},
|
|
1081
|
-
app: await this.challenge.resolveAppKeyFromOrigin(requestOrigin),
|
|
1082
|
-
}
|
|
1083
|
-
);
|
|
1369
|
+
if (newDevice) {
|
|
1370
|
+
const user = await this.prisma.user.findFirst({ where: { id: userId } });
|
|
1371
|
+
await this.sendNewDeviceLoginMail(locale, user, payload.email, ipAddress, userAgent, requestOrigin);
|
|
1372
|
+
}
|
|
1084
1373
|
|
|
1085
1374
|
await this.user.registerUserActivity(userId, 'login');
|
|
1086
1375
|
const requiresPasswordReset = await this.getRequiresPasswordResetByUserId(
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { getLocaleText } from '@hed-hog/api-locale';
|
|
2
|
+
import { IsNotEmpty, IsOptional, IsString } from 'class-validator';
|
|
3
|
+
import { IsEmailWithSettings } from '../../validators/is-email-with-settings.validator';
|
|
4
|
+
|
|
5
|
+
export class ResendConfirmationDTO {
|
|
6
|
+
@IsNotEmpty({ message: (args) => getLocaleText('validation.currentEmailRequired', args.value) })
|
|
7
|
+
@IsEmailWithSettings({ message: (args) => getLocaleText('validation.currentEmailInvalid', args.value) })
|
|
8
|
+
set email(value: string) {
|
|
9
|
+
this._email = value?.trim().toLowerCase();
|
|
10
|
+
}
|
|
11
|
+
get email(): string {
|
|
12
|
+
return this._email;
|
|
13
|
+
}
|
|
14
|
+
private _email: string;
|
|
15
|
+
|
|
16
|
+
/** Chave do app de origem (ex.: "class"), usada para montar a URL do link. */
|
|
17
|
+
@IsOptional()
|
|
18
|
+
@IsString()
|
|
19
|
+
app?: string;
|
|
20
|
+
}
|
|
@@ -85,6 +85,7 @@ export class AuthorizeController {
|
|
|
85
85
|
@Locale() locale: string,
|
|
86
86
|
@Ip() ipAddress: string,
|
|
87
87
|
@Headers('user-agent') userAgent: string,
|
|
88
|
+
@Headers('x-browser-id') browserId: string,
|
|
88
89
|
) {
|
|
89
90
|
return this.service.exchangeCode({
|
|
90
91
|
code: body.code,
|
|
@@ -94,6 +95,7 @@ export class AuthorizeController {
|
|
|
94
95
|
locale,
|
|
95
96
|
ipAddress,
|
|
96
97
|
userAgent,
|
|
98
|
+
browserId: browserId || null,
|
|
97
99
|
});
|
|
98
100
|
}
|
|
99
101
|
}
|
|
@@ -191,6 +191,29 @@ describe('AuthorizeService', () => {
|
|
|
191
191
|
7,
|
|
192
192
|
'127.0.0.1',
|
|
193
193
|
'jest',
|
|
194
|
+
undefined,
|
|
195
|
+
);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it('repassa o browserId, que identifica o dispositivo da extensão', async () => {
|
|
199
|
+
const verifier = makeVerifier();
|
|
200
|
+
deps.prisma.$queryRaw.mockResolvedValue([
|
|
201
|
+
{ user_id: 7, code_challenge: sha256b64url(verifier), scope: 'vaults' },
|
|
202
|
+
] as never);
|
|
203
|
+
|
|
204
|
+
await service.exchangeCode({
|
|
205
|
+
...validExchangeParams('CODE', verifier),
|
|
206
|
+
browserId: 'ext-install-1',
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// Sem isto o dispositivo cairia no User-Agent — o do Chrome inteiro, que
|
|
210
|
+
// muda a cada atualização do navegador.
|
|
211
|
+
expect(deps.auth.getAuthenticationPayload).toHaveBeenCalledWith(
|
|
212
|
+
'en',
|
|
213
|
+
7,
|
|
214
|
+
'127.0.0.1',
|
|
215
|
+
'jest',
|
|
216
|
+
'ext-install-1',
|
|
194
217
|
);
|
|
195
218
|
});
|
|
196
219
|
|
|
@@ -168,6 +168,13 @@ export class AuthorizeService {
|
|
|
168
168
|
locale: string;
|
|
169
169
|
ipAddress: string;
|
|
170
170
|
userAgent: string;
|
|
171
|
+
/**
|
|
172
|
+
* Id estavel da instalacao do cliente (header `X-Browser-ID`). Sem ele o
|
|
173
|
+
* dispositivo e identificado pelo User-Agent, que numa extensao e o do
|
|
174
|
+
* navegador inteiro — muda a cada atualizacao do Chrome e enche a lista de
|
|
175
|
+
* dispositivos do usuario de entradas repetidas.
|
|
176
|
+
*/
|
|
177
|
+
browserId?: string | null;
|
|
171
178
|
}) {
|
|
172
179
|
const client = await this.getClient(params.clientId);
|
|
173
180
|
|
|
@@ -212,6 +219,7 @@ export class AuthorizeService {
|
|
|
212
219
|
row.user_id,
|
|
213
220
|
params.ipAddress,
|
|
214
221
|
params.userAgent,
|
|
222
|
+
params.browserId,
|
|
215
223
|
);
|
|
216
224
|
|
|
217
225
|
return {
|