@hed-hog/core 0.0.399 → 0.0.400
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/dist/ai/ai-agent.sort.d.ts +6 -0
- package/dist/ai/ai-agent.sort.d.ts.map +1 -0
- package/dist/ai/ai-agent.sort.js +21 -0
- package/dist/ai/ai-agent.sort.js.map +1 -0
- package/dist/ai/ai.controller.d.ts +4 -4
- package/dist/ai/ai.service.d.ts +35 -4
- package/dist/ai/ai.service.d.ts.map +1 -1
- package/dist/ai/ai.service.js +51 -137
- package/dist/ai/ai.service.js.map +1 -1
- package/dist/ai-instruction/ai-instruction.service.d.ts.map +1 -1
- package/dist/ai-instruction/ai-instruction.service.js +26 -23
- package/dist/ai-instruction/ai-instruction.service.js.map +1 -1
- package/dist/ai-instruction/ai-instruction.sort.d.ts +16 -0
- package/dist/ai-instruction/ai-instruction.sort.d.ts.map +1 -0
- package/dist/ai-instruction/ai-instruction.sort.js +32 -0
- package/dist/ai-instruction/ai-instruction.sort.js.map +1 -0
- package/dist/auth/auth.controller.d.ts +24 -8
- package/dist/auth/auth.controller.d.ts.map +1 -1
- package/dist/auth/auth.module.d.ts.map +1 -1
- package/dist/auth/auth.module.js +8 -1
- package/dist/auth/auth.module.js.map +1 -1
- package/dist/auth/auth.service.d.ts +72 -25
- package/dist/auth/auth.service.d.ts.map +1 -1
- package/dist/auth/auth.service.js +41 -4
- package/dist/auth/auth.service.js.map +1 -1
- package/dist/auth/guards/auth.guard.d.ts +3 -1
- package/dist/auth/guards/auth.guard.d.ts.map +1 -1
- package/dist/auth/guards/auth.guard.js +12 -3
- package/dist/auth/guards/auth.guard.js.map +1 -1
- package/dist/authorize/authorize.controller.d.ts +29 -5
- package/dist/authorize/authorize.controller.d.ts.map +1 -1
- package/dist/authorize/authorize.controller.js +120 -13
- package/dist/authorize/authorize.controller.js.map +1 -1
- package/dist/authorize/authorize.errors.d.ts +34 -0
- package/dist/authorize/authorize.errors.d.ts.map +1 -0
- package/dist/authorize/authorize.errors.js +37 -0
- package/dist/authorize/authorize.errors.js.map +1 -0
- package/dist/authorize/authorize.module.d.ts.map +1 -1
- package/dist/authorize/authorize.module.js +6 -1
- package/dist/authorize/authorize.module.js.map +1 -1
- package/dist/authorize/authorize.service.d.ts +77 -3
- package/dist/authorize/authorize.service.d.ts.map +1 -1
- package/dist/authorize/authorize.service.js +212 -21
- package/dist/authorize/authorize.service.js.map +1 -1
- package/dist/authorize/dto/authorize-info.dto.d.ts +10 -1
- package/dist/authorize/dto/authorize-info.dto.d.ts.map +1 -1
- package/dist/authorize/dto/authorize-info.dto.js +16 -1
- package/dist/authorize/dto/authorize-info.dto.js.map +1 -1
- package/dist/authorize/dto/consent.dto.d.ts +10 -1
- package/dist/authorize/dto/consent.dto.d.ts.map +1 -1
- package/dist/authorize/dto/consent.dto.js +16 -1
- package/dist/authorize/dto/consent.dto.js.map +1 -1
- package/dist/authorize/dto/token.dto.d.ts +12 -0
- package/dist/authorize/dto/token.dto.d.ts.map +1 -1
- package/dist/authorize/dto/token.dto.js +21 -1
- package/dist/authorize/dto/token.dto.js.map +1 -1
- package/dist/authorize/oauth-exception.filter.d.ts +17 -0
- package/dist/authorize/oauth-exception.filter.d.ts.map +1 -0
- package/dist/authorize/oauth-exception.filter.js +54 -0
- package/dist/authorize/oauth-exception.filter.js.map +1 -0
- package/dist/authorize/oauth-metadata.service.d.ts +39 -0
- package/dist/authorize/oauth-metadata.service.d.ts.map +1 -0
- package/dist/authorize/oauth-metadata.service.js +81 -0
- package/dist/authorize/oauth-metadata.service.js.map +1 -0
- package/dist/authorize/well-known.controller.d.ts +67 -0
- package/dist/authorize/well-known.controller.d.ts.map +1 -0
- package/dist/authorize/well-known.controller.js +126 -0
- package/dist/authorize/well-known.controller.js.map +1 -0
- package/dist/captcha/captcha.service.d.ts +32 -2
- package/dist/captcha/captcha.service.d.ts.map +1 -1
- package/dist/captcha/captcha.service.js +85 -12
- package/dist/captcha/captcha.service.js.map +1 -1
- package/dist/challenge/challenge.service.d.ts +2 -2
- package/dist/core.module.d.ts.map +1 -1
- package/dist/core.module.js +6 -0
- package/dist/core.module.js.map +1 -1
- package/dist/dashboard/dashboard/dashboard.controller.d.ts +9 -9
- package/dist/dashboard/dashboard/dashboard.service.d.ts +9 -9
- package/dist/dashboard/dashboard-component/dashboard-component.controller.d.ts +3 -3
- package/dist/dashboard/dashboard-component/dashboard-component.service.d.ts +3 -3
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.controller.d.ts +3 -3
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.d.ts +3 -3
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.d.ts.map +1 -1
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.js +2 -4
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.js.map +1 -1
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.sort.d.ts +8 -0
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.sort.d.ts.map +1 -0
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.sort.js +18 -0
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.sort.js.map +1 -0
- package/dist/dashboard/dashboard-item/dashboard-item.service.d.ts.map +1 -1
- package/dist/dashboard/dashboard-item/dashboard-item.service.js +2 -4
- package/dist/dashboard/dashboard-item/dashboard-item.service.js.map +1 -1
- package/dist/dashboard/dashboard-item/dashboard-item.sort.d.ts +9 -0
- package/dist/dashboard/dashboard-item/dashboard-item.sort.d.ts.map +1 -0
- package/dist/dashboard/dashboard-item/dashboard-item.sort.js +19 -0
- package/dist/dashboard/dashboard-item/dashboard-item.sort.js.map +1 -0
- package/dist/dashboard/dashboard-role/dashboard-role.controller.d.ts +3 -3
- package/dist/dashboard/dashboard-role/dashboard-role.service.d.ts +3 -3
- package/dist/dashboard/dashboard-role/dashboard-role.service.d.ts.map +1 -1
- package/dist/dashboard/dashboard-role/dashboard-role.service.js +2 -4
- package/dist/dashboard/dashboard-role/dashboard-role.service.js.map +1 -1
- package/dist/dashboard/dashboard-role/dashboard-role.sort.d.ts +8 -0
- package/dist/dashboard/dashboard-role/dashboard-role.sort.d.ts.map +1 -0
- package/dist/dashboard/dashboard-role/dashboard-role.sort.js +16 -0
- package/dist/dashboard/dashboard-role/dashboard-role.sort.js.map +1 -0
- package/dist/file/file-upload-ticket.service.d.ts +160 -0
- package/dist/file/file-upload-ticket.service.d.ts.map +1 -0
- package/dist/file/file-upload-ticket.service.js +382 -0
- package/dist/file/file-upload-ticket.service.js.map +1 -0
- package/dist/file/file.controller.d.ts +24 -1
- package/dist/file/file.controller.d.ts.map +1 -1
- package/dist/file/file.controller.js +63 -2
- package/dist/file/file.controller.js.map +1 -1
- package/dist/file/file.module.d.ts.map +1 -1
- package/dist/file/file.module.js +14 -2
- package/dist/file/file.module.js.map +1 -1
- package/dist/file/file.service.d.ts +182 -6
- package/dist/file/file.service.d.ts.map +1 -1
- package/dist/file/file.service.js +386 -54
- package/dist/file/file.service.js.map +1 -1
- package/dist/file/filename-encoding.util.d.ts +24 -0
- package/dist/file/filename-encoding.util.d.ts.map +1 -0
- package/dist/file/filename-encoding.util.js +63 -0
- package/dist/file/filename-encoding.util.js.map +1 -0
- package/dist/file/image-optimization.service.d.ts +27 -0
- package/dist/file/image-optimization.service.d.ts.map +1 -1
- package/dist/file/image-optimization.service.js +63 -14
- package/dist/file/image-optimization.service.js.map +1 -1
- package/dist/file/mcp-file-source.schema.d.ts +33 -0
- package/dist/file/mcp-file-source.schema.d.ts.map +1 -0
- package/dist/file/mcp-file-source.schema.js +75 -0
- package/dist/file/mcp-file-source.schema.js.map +1 -0
- package/dist/file/mcp-file-source.service.d.ts +124 -0
- package/dist/file/mcp-file-source.service.d.ts.map +1 -0
- package/dist/file/mcp-file-source.service.js +262 -0
- package/dist/file/mcp-file-source.service.js.map +1 -0
- package/dist/file/mimetype-sniff.util.d.ts +62 -0
- package/dist/file/mimetype-sniff.util.d.ts.map +1 -0
- package/dist/file/mimetype-sniff.util.js +226 -0
- package/dist/file/mimetype-sniff.util.js.map +1 -0
- package/dist/file/remote-fetch.util.d.ts +35 -0
- package/dist/file/remote-fetch.util.d.ts.map +1 -0
- package/dist/file/remote-fetch.util.js +236 -0
- package/dist/file/remote-fetch.util.js.map +1 -0
- package/dist/impersonation/dto/start-impersonation.dto.d.ts +4 -1
- package/dist/impersonation/dto/start-impersonation.dto.d.ts.map +1 -1
- package/dist/impersonation/dto/start-impersonation.dto.js +1 -1
- package/dist/impersonation/dto/start-impersonation.dto.js.map +1 -1
- package/dist/impersonation/impersonation.service.d.ts +8 -3
- package/dist/impersonation/impersonation.service.d.ts.map +1 -1
- package/dist/impersonation/impersonation.service.js +14 -4
- package/dist/impersonation/impersonation.service.js.map +1 -1
- package/dist/incident/incident.controller.d.ts +2 -1
- package/dist/incident/incident.controller.d.ts.map +1 -1
- package/dist/incident/incident.controller.js +10 -6
- package/dist/incident/incident.controller.js.map +1 -1
- package/dist/incident/incident.service.d.ts +2 -0
- package/dist/incident/incident.service.d.ts.map +1 -1
- package/dist/incident/incident.service.js +4 -0
- package/dist/incident/incident.service.js.map +1 -1
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -1
- package/dist/integration-profile/integration-profile.secrets.d.ts.map +1 -1
- package/dist/integration-profile/integration-profile.secrets.js +7 -0
- package/dist/integration-profile/integration-profile.secrets.js.map +1 -1
- package/dist/integration-profile/integration-profile.service.d.ts +0 -13
- package/dist/integration-profile/integration-profile.service.d.ts.map +1 -1
- package/dist/integration-profile/integration-profile.service.js +5 -40
- package/dist/integration-profile/integration-profile.service.js.map +1 -1
- package/dist/integration-profile/integration-profile.utils.js +5 -5
- package/dist/integration-profile/integration-profile.utils.js.map +1 -1
- package/dist/integration-profile/testers/index.d.ts.map +1 -1
- package/dist/integration-profile/testers/index.js +2 -0
- package/dist/integration-profile/testers/index.js.map +1 -1
- package/dist/integration-profile/testers/shipping.testers.d.ts +3 -0
- package/dist/integration-profile/testers/shipping.testers.d.ts.map +1 -0
- package/dist/integration-profile/testers/shipping.testers.js +71 -0
- package/dist/integration-profile/testers/shipping.testers.js.map +1 -0
- package/dist/locale/locale.controller.d.ts +1 -1
- package/dist/locale/locale.service.d.ts +1 -1
- package/dist/mail/dto/send.dto.d.ts +6 -1
- package/dist/mail/dto/send.dto.d.ts.map +1 -1
- package/dist/mail/dto/send.dto.js.map +1 -1
- package/dist/mail/mail.service.d.ts +9 -0
- package/dist/mail/mail.service.d.ts.map +1 -1
- package/dist/mail/mail.service.js +34 -14
- package/dist/mail/mail.service.js.map +1 -1
- package/dist/mail-sent/mail-sent.controller.d.ts +1 -1
- package/dist/mail-sent/mail-sent.service.d.ts +1 -1
- package/dist/mail-sent/mail-sent.service.d.ts.map +1 -1
- package/dist/mail-sent/mail-sent.service.js +2 -1
- package/dist/mail-sent/mail-sent.service.js.map +1 -1
- package/dist/mail-sent/mail-sent.sort.d.ts +9 -0
- package/dist/mail-sent/mail-sent.sort.d.ts.map +1 -0
- package/dist/mail-sent/mail-sent.sort.js +19 -0
- package/dist/mail-sent/mail-sent.sort.js.map +1 -0
- package/dist/mcp/filters/mcp-challenge.filter.d.ts +20 -0
- package/dist/mcp/filters/mcp-challenge.filter.d.ts.map +1 -0
- package/dist/mcp/filters/mcp-challenge.filter.js +69 -0
- package/dist/mcp/filters/mcp-challenge.filter.js.map +1 -0
- package/dist/mcp/guards/mcp-auth.guard.d.ts +4 -1
- package/dist/mcp/guards/mcp-auth.guard.d.ts.map +1 -1
- package/dist/mcp/guards/mcp-auth.guard.js +19 -5
- package/dist/mcp/guards/mcp-auth.guard.js.map +1 -1
- package/dist/mcp/guards/mcp-permission.guard.d.ts +25 -3
- package/dist/mcp/guards/mcp-permission.guard.d.ts.map +1 -1
- package/dist/mcp/guards/mcp-permission.guard.js +46 -26
- package/dist/mcp/guards/mcp-permission.guard.js.map +1 -1
- package/dist/mcp/mcp-audit.service.d.ts +17 -0
- package/dist/mcp/mcp-audit.service.d.ts.map +1 -1
- package/dist/mcp/mcp-audit.service.js +32 -3
- package/dist/mcp/mcp-audit.service.js.map +1 -1
- package/dist/mcp/mcp-permission.service.d.ts +64 -0
- package/dist/mcp/mcp-permission.service.d.ts.map +1 -0
- package/dist/mcp/mcp-permission.service.js +178 -0
- package/dist/mcp/mcp-permission.service.js.map +1 -0
- package/dist/mcp/mcp-server.service.d.ts +3 -1
- package/dist/mcp/mcp-server.service.d.ts.map +1 -1
- package/dist/mcp/mcp-server.service.js +36 -15
- package/dist/mcp/mcp-server.service.js.map +1 -1
- package/dist/mcp/mcp.controller.d.ts.map +1 -1
- package/dist/mcp/mcp.controller.js +10 -2
- package/dist/mcp/mcp.controller.js.map +1 -1
- package/dist/mcp/mcp.module.d.ts.map +1 -1
- package/dist/mcp/mcp.module.js +5 -1
- package/dist/mcp/mcp.module.js.map +1 -1
- package/dist/mcp/tools/core-ai.mcp-tools.js +2 -9
- package/dist/mcp/tools/core-ai.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-dashboard-items.mcp-tools.js +1 -1
- package/dist/mcp/tools/core-dashboard-items.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-dashboard.mcp-tools.js +2 -9
- package/dist/mcp/tools/core-dashboard.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-event-webhooks.mcp-tools.d.ts.map +1 -1
- package/dist/mcp/tools/core-event-webhooks.mcp-tools.js +2 -10
- package/dist/mcp/tools/core-event-webhooks.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-files.mcp-tools.d.ts +17 -5
- package/dist/mcp/tools/core-files.mcp-tools.d.ts.map +1 -1
- package/dist/mcp/tools/core-files.mcp-tools.js +186 -24
- package/dist/mcp/tools/core-files.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-integration-events.mcp-tools.js +2 -9
- package/dist/mcp/tools/core-integration-events.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-mail-sent.mcp-tools.js +2 -14
- package/dist/mcp/tools/core-mail-sent.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-mail.mcp-tools.js +2 -9
- package/dist/mcp/tools/core-mail.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-menus.mcp-tools.d.ts.map +1 -1
- package/dist/mcp/tools/core-menus.mcp-tools.js +2 -10
- package/dist/mcp/tools/core-menus.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-notifications.mcp-tools.d.ts.map +1 -1
- package/dist/mcp/tools/core-notifications.mcp-tools.js +0 -1
- package/dist/mcp/tools/core-notifications.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-roles.mcp-tools.d.ts.map +1 -1
- package/dist/mcp/tools/core-roles.mcp-tools.js +2 -13
- package/dist/mcp/tools/core-roles.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-routes.mcp-tools.d.ts.map +1 -1
- package/dist/mcp/tools/core-routes.mcp-tools.js +2 -11
- package/dist/mcp/tools/core-routes.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-screens.mcp-tools.d.ts.map +1 -1
- package/dist/mcp/tools/core-screens.mcp-tools.js +2 -11
- package/dist/mcp/tools/core-screens.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-sessions.mcp-tools.d.ts.map +1 -1
- package/dist/mcp/tools/core-sessions.mcp-tools.js +0 -2
- package/dist/mcp/tools/core-sessions.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-settings.mcp-tools.d.ts.map +1 -1
- package/dist/mcp/tools/core-settings.mcp-tools.js +2 -10
- package/dist/mcp/tools/core-settings.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-users.mcp-tools.js +2 -9
- package/dist/mcp/tools/core-users.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-webhooks.mcp-tools.d.ts.map +1 -1
- package/dist/mcp/tools/core-webhooks.mcp-tools.js +2 -10
- package/dist/mcp/tools/core-webhooks.mcp-tools.js.map +1 -1
- package/dist/mcp/types/mcp-context.type.d.ts +9 -0
- package/dist/mcp/types/mcp-context.type.d.ts.map +1 -1
- package/dist/mcp-chat/mcp-chat.service.d.ts.map +1 -1
- package/dist/mcp-chat/mcp-chat.service.js +8 -0
- package/dist/mcp-chat/mcp-chat.service.js.map +1 -1
- package/dist/meeting/meeting-provider.registry.d.ts +115 -0
- package/dist/meeting/meeting-provider.registry.d.ts.map +1 -1
- package/dist/meeting/meeting-provider.registry.js.map +1 -1
- package/dist/meeting/meeting.constants.d.ts +70 -0
- package/dist/meeting/meeting.constants.d.ts.map +1 -1
- package/dist/meeting/meeting.constants.js +9 -1
- package/dist/meeting/meeting.constants.js.map +1 -1
- package/dist/menu/menu.service.d.ts.map +1 -1
- package/dist/menu/menu.service.js +29 -9
- package/dist/menu/menu.service.js.map +1 -1
- package/dist/message-template/dto/update-message-template.dto.d.ts +18 -0
- package/dist/message-template/dto/update-message-template.dto.d.ts.map +1 -0
- package/dist/message-template/dto/update-message-template.dto.js +38 -0
- package/dist/message-template/dto/update-message-template.dto.js.map +1 -0
- package/dist/message-template/message-template-admin.service.d.ts +70 -0
- package/dist/message-template/message-template-admin.service.d.ts.map +1 -0
- package/dist/message-template/message-template-admin.service.js +226 -0
- package/dist/message-template/message-template-admin.service.js.map +1 -0
- package/dist/message-template/message-template.controller.d.ts +29 -0
- package/dist/message-template/message-template.controller.d.ts.map +1 -0
- package/dist/message-template/message-template.controller.js +101 -0
- package/dist/message-template/message-template.controller.js.map +1 -0
- package/dist/message-template/message-template.module.d.ts +10 -0
- package/dist/message-template/message-template.module.d.ts.map +1 -0
- package/dist/message-template/message-template.module.js +33 -0
- package/dist/message-template/message-template.module.js.map +1 -0
- package/dist/message-template/message-template.registry.d.ts +55 -0
- package/dist/message-template/message-template.registry.d.ts.map +1 -0
- package/dist/message-template/message-template.registry.js +64 -0
- package/dist/message-template/message-template.registry.js.map +1 -0
- package/dist/message-template/message-template.service.d.ts +59 -0
- package/dist/message-template/message-template.service.d.ts.map +1 -0
- package/dist/message-template/message-template.service.js +166 -0
- package/dist/message-template/message-template.service.js.map +1 -0
- package/dist/notification/notification.constants.d.ts +11 -0
- package/dist/notification/notification.constants.d.ts.map +1 -0
- package/dist/notification/notification.constants.js +14 -0
- package/dist/notification/notification.constants.js.map +1 -0
- package/dist/notification/notification.controller.d.ts +20 -20
- package/dist/notification/notification.module.d.ts.map +1 -1
- package/dist/notification/notification.module.js +3 -0
- package/dist/notification/notification.module.js.map +1 -1
- package/dist/notification/notification.service.d.ts +33 -21
- package/dist/notification/notification.service.d.ts.map +1 -1
- package/dist/notification/notification.service.js +36 -2
- package/dist/notification/notification.service.js.map +1 -1
- package/dist/oauth/oauth.controller.d.ts +4 -6
- package/dist/oauth/oauth.controller.d.ts.map +1 -1
- package/dist/oauth/oauth.controller.js +0 -1
- package/dist/oauth/oauth.controller.js.map +1 -1
- package/dist/oauth/oauth.errors.d.ts +15 -1
- package/dist/oauth/oauth.errors.d.ts.map +1 -1
- package/dist/oauth/oauth.errors.js +9 -1
- package/dist/oauth/oauth.errors.js.map +1 -1
- package/dist/oauth/oauth.module.d.ts.map +1 -1
- package/dist/oauth/oauth.module.js +6 -1
- package/dist/oauth/oauth.module.js.map +1 -1
- package/dist/oauth/oauth.service.d.ts +25 -5
- package/dist/oauth/oauth.service.d.ts.map +1 -1
- package/dist/oauth/oauth.service.js +63 -27
- package/dist/oauth/oauth.service.js.map +1 -1
- package/dist/oauth/providers/abstract.provider.d.ts +10 -0
- package/dist/oauth/providers/abstract.provider.d.ts.map +1 -1
- package/dist/oauth/providers/abstract.provider.js +39 -12
- package/dist/oauth/providers/abstract.provider.js.map +1 -1
- package/dist/oauth/providers/github.provider.d.ts +1 -1
- package/dist/oauth/providers/github.provider.d.ts.map +1 -1
- package/dist/oauth/providers/github.provider.js +48 -22
- package/dist/oauth/providers/github.provider.js.map +1 -1
- package/dist/oauth/providers/microsoft-entra-id.provider.d.ts.map +1 -1
- package/dist/oauth/providers/microsoft-entra-id.provider.js +6 -0
- package/dist/oauth/providers/microsoft-entra-id.provider.js.map +1 -1
- package/dist/observability/monitored-cron.d.ts +29 -12
- package/dist/observability/monitored-cron.d.ts.map +1 -1
- package/dist/observability/monitored-cron.js +131 -46
- package/dist/observability/monitored-cron.js.map +1 -1
- package/dist/pairing/pairing.service.d.ts +6 -2
- package/dist/pairing/pairing.service.d.ts.map +1 -1
- package/dist/profile/dto/change-password.dto.d.ts.map +1 -1
- package/dist/profile/dto/change-password.dto.js +4 -6
- package/dist/profile/dto/change-password.dto.js.map +1 -1
- package/dist/profile/profile.controller.d.ts +1 -1
- package/dist/profile/profile.service.d.ts +1 -1
- package/dist/profile/profile.service.d.ts.map +1 -1
- package/dist/profile/profile.service.js +9 -3
- package/dist/profile/profile.service.js.map +1 -1
- package/dist/realtime/realtime-tick.coalescer.d.ts +34 -0
- package/dist/realtime/realtime-tick.coalescer.d.ts.map +1 -0
- package/dist/realtime/realtime-tick.coalescer.js +60 -0
- package/dist/realtime/realtime-tick.coalescer.js.map +1 -0
- package/dist/realtime/realtime.service.d.ts +2 -0
- package/dist/realtime/realtime.service.d.ts.map +1 -1
- package/dist/realtime/realtime.service.js +22 -2
- package/dist/realtime/realtime.service.js.map +1 -1
- package/dist/redis/redis-kv.service.d.ts +90 -0
- package/dist/redis/redis-kv.service.d.ts.map +1 -0
- package/dist/redis/redis-kv.service.js +247 -0
- package/dist/redis/redis-kv.service.js.map +1 -0
- package/dist/redis/redis-master.d.ts +29 -0
- package/dist/redis/redis-master.d.ts.map +1 -0
- package/dist/redis/redis-master.js +81 -0
- package/dist/redis/redis-master.js.map +1 -0
- package/dist/redis/redis-pubsub.d.ts +12 -0
- package/dist/redis/redis-pubsub.d.ts.map +1 -1
- package/dist/redis/redis-pubsub.js +25 -2
- package/dist/redis/redis-pubsub.js.map +1 -1
- package/dist/redis/redis.module.d.ts +11 -0
- package/dist/redis/redis.module.d.ts.map +1 -0
- package/dist/redis/redis.module.js +30 -0
- package/dist/redis/redis.module.js.map +1 -0
- package/dist/session/session.controller.d.ts +6 -2
- package/dist/session/session.controller.d.ts.map +1 -1
- package/dist/session/session.service.d.ts +118 -15
- package/dist/session/session.service.d.ts.map +1 -1
- package/dist/session/session.service.js +251 -77
- package/dist/session/session.service.js.map +1 -1
- package/dist/setting/setting.controller.d.ts +5 -0
- package/dist/setting/setting.controller.d.ts.map +1 -1
- package/dist/setting/setting.service.d.ts +7 -0
- package/dist/setting/setting.service.d.ts.map +1 -1
- package/dist/setting/setting.service.js +38 -3
- package/dist/setting/setting.service.js.map +1 -1
- package/dist/task/task.service.d.ts +17 -0
- package/dist/task/task.service.d.ts.map +1 -1
- package/dist/task/task.service.js +48 -13
- package/dist/task/task.service.js.map +1 -1
- package/dist/token/token.service.d.ts +19 -1
- package/dist/token/token.service.d.ts.map +1 -1
- package/dist/token/token.service.js +39 -2
- package/dist/token/token.service.js.map +1 -1
- package/dist/user/user-credential.util.d.ts +10 -0
- package/dist/user/user-credential.util.d.ts.map +1 -1
- package/dist/user/user-credential.util.js +14 -1
- package/dist/user/user-credential.util.js.map +1 -1
- package/dist/user/user.service.d.ts +12 -4
- package/dist/user/user.service.d.ts.map +1 -1
- package/dist/user/user.service.js +2 -11
- package/dist/user/user.service.js.map +1 -1
- package/dist/user/user.sort.d.ts +16 -0
- package/dist/user/user.sort.d.ts.map +1 -0
- package/dist/user/user.sort.js +25 -0
- package/dist/user/user.sort.js.map +1 -0
- package/dist/utils/abbreviate-name.d.ts +13 -0
- package/dist/utils/abbreviate-name.d.ts.map +1 -0
- package/dist/utils/abbreviate-name.js +42 -0
- package/dist/utils/abbreviate-name.js.map +1 -0
- package/dist/utils/date-range-query.dto.d.ts +11 -0
- package/dist/utils/date-range-query.dto.d.ts.map +1 -0
- package/dist/utils/date-range-query.dto.js +63 -0
- package/dist/utils/date-range-query.dto.js.map +1 -0
- package/dist/utils/date-range.d.ts +96 -0
- package/dist/utils/date-range.d.ts.map +1 -0
- package/dist/utils/date-range.js +172 -0
- package/dist/utils/date-range.js.map +1 -0
- package/dist/webhook-integration/webhook-utils.d.ts.map +1 -1
- package/dist/webhook-integration/webhook-utils.js +24 -0
- package/dist/webhook-integration/webhook-utils.js.map +1 -1
- package/dist/whatsapp-sender/whatsapp-recipients.d.ts +49 -0
- package/dist/whatsapp-sender/whatsapp-recipients.d.ts.map +1 -0
- package/dist/whatsapp-sender/whatsapp-recipients.js +66 -0
- package/dist/whatsapp-sender/whatsapp-recipients.js.map +1 -0
- package/dist/whatsapp-sender/whatsapp-sender.service.d.ts +25 -0
- package/dist/whatsapp-sender/whatsapp-sender.service.d.ts.map +1 -1
- package/dist/whatsapp-sender/whatsapp-sender.service.js +46 -5
- package/dist/whatsapp-sender/whatsapp-sender.service.js.map +1 -1
- package/hedhog/data/integration_provider.yaml +9 -0
- package/hedhog/data/integration_type.yaml +6 -0
- package/hedhog/data/locale.yaml +0 -1
- package/hedhog/data/mail.yaml +2 -2
- package/hedhog/data/mail_layout.yaml +6 -3
- package/hedhog/data/menu.yaml +25 -5
- package/hedhog/data/route.yaml +140 -0
- package/hedhog/data/setting_group.yaml +137 -3
- package/hedhog/frontend/app/access-log/page.tsx.ejs +47 -53
- package/hedhog/frontend/app/account/components/active-sessions.tsx.ejs +33 -8
- package/hedhog/frontend/app/account/components/mcp-tokens.tsx.ejs +54 -13
- package/hedhog/frontend/app/account/components/mfa-method-card.tsx.ejs +7 -1
- package/hedhog/frontend/app/account/components/recovery-codes-dialog.tsx.ejs +1 -0
- package/hedhog/frontend/app/account/components/tokens.tsx.ejs +47 -17
- package/hedhog/frontend/app/ai_agent/page.tsx.ejs +46 -5
- package/hedhog/frontend/app/ai_instruction/_components/ai-instruction-form.tsx.ejs +101 -30
- package/hedhog/frontend/app/ai_instruction/_components/ai-instruction-versions.tsx.ejs +24 -8
- package/hedhog/frontend/app/ai_instruction/page.tsx.ejs +80 -15
- package/hedhog/frontend/app/configurations/[slug]/components/image-optimization-action.tsx.ejs +166 -0
- package/hedhog/frontend/app/configurations/[slug]/components/setting-field.test.tsx.ejs +145 -45
- package/hedhog/frontend/app/configurations/[slug]/components/setting-field.tsx.ejs +99 -219
- package/hedhog/frontend/app/configurations/[slug]/page.tsx.ejs +19 -1
- package/hedhog/frontend/app/configurations/layout.tsx.ejs +19 -5
- package/hedhog/frontend/app/dashboard/[slug]/widget-renderer.tsx.ejs +2 -8
- package/hedhog/frontend/app/dashboard/components/dynamic-widget.tsx.ejs +3 -0
- package/hedhog/frontend/app/dashboard/components/widget-wrapper.tsx.ejs +4 -0
- package/hedhog/frontend/app/dashboard/dashboard-home-tabs.tsx.ejs +43 -44
- package/hedhog/frontend/app/dashboard/management/tabs/component-roles-tab.tsx.ejs +41 -3
- package/hedhog/frontend/app/dashboard/management/tabs/components-tab.tsx.ejs +42 -2
- package/hedhog/frontend/app/dashboard/management/tabs/dashboard-roles-tab.tsx.ejs +41 -3
- package/hedhog/frontend/app/dashboard/management/tabs/dashboards-tab.tsx.ejs +39 -4
- package/hedhog/frontend/app/dashboard/management/tabs/items-tab.tsx.ejs +42 -3
- package/hedhog/frontend/app/incidents/page.tsx.ejs +60 -31
- package/hedhog/frontend/app/integration/profiles/page.tsx.ejs +45 -5
- package/hedhog/frontend/app/integrations-webhooks/page.tsx.ejs +92 -15
- package/hedhog/frontend/app/mail/log/page.tsx.ejs +109 -61
- package/hedhog/frontend/app/mail/template/page.tsx.ejs +125 -23
- package/hedhog/frontend/app/mcp_chat/chat-components.tsx.ejs +13 -15
- package/hedhog/frontend/app/mcp_chat/page.tsx.ejs +116 -110
- package/hedhog/frontend/app/message-templates/_components/message-template-sheet.tsx.ejs +245 -0
- package/hedhog/frontend/app/message-templates/_lib/types.ts.ejs +24 -0
- package/hedhog/frontend/app/message-templates/page.tsx.ejs +182 -0
- package/hedhog/frontend/app/pages/_components/page-wizard-sheet.tsx.ejs +1 -0
- package/hedhog/frontend/app/pages/page.tsx.ejs +94 -9
- package/hedhog/frontend/app/roles/menus.tsx.ejs +4 -4
- package/hedhog/frontend/app/roles/routes.tsx.ejs +4 -4
- package/hedhog/frontend/app/roles/users.tsx.ejs +4 -4
- package/hedhog/frontend/app/setting-list/page.tsx.ejs +71 -9
- package/hedhog/frontend/app/users/page.tsx.ejs +77 -67
- package/hedhog/frontend/messages/en.json +183 -36
- package/hedhog/frontend/messages/pt.json +183 -36
- package/hedhog/table/cron_lock.yaml +17 -0
- package/hedhog/table/file.yaml +13 -1
- package/hedhog/table/file_upload_chunk.yaml +23 -0
- package/hedhog/table/file_upload_ticket.yaml +67 -0
- package/hedhog/table/mcp_tool_call_log.yaml +30 -22
- package/hedhog/table/message_template.yaml +33 -0
- package/hedhog/table/setting.yaml +8 -0
- package/hedhog/table/user_session.yaml +36 -1
- package/package.json +7 -5
- package/src/ai/ai-agent.sort.ts +21 -0
- package/src/ai/ai.service.spec.ts +173 -1
- package/src/ai/ai.service.ts +64 -193
- package/src/ai-instruction/ai-instruction.service.ts +32 -28
- package/src/ai-instruction/ai-instruction.sort.ts +32 -0
- package/src/auth/auth.module.ts +8 -1
- package/src/auth/auth.service.spec.ts +108 -1
- package/src/auth/auth.service.ts +59 -5
- package/src/auth/guards/auth.guard.ts +9 -1
- package/src/authorize/authorize.controller.ts +126 -11
- package/src/authorize/authorize.errors.ts +55 -0
- package/src/authorize/authorize.module.ts +6 -1
- package/src/authorize/authorize.service.spec.ts +379 -7
- package/src/authorize/authorize.service.ts +309 -23
- package/src/authorize/dto/authorize-info.dto.ts +21 -3
- package/src/authorize/dto/consent.dto.ts +21 -3
- package/src/authorize/dto/token.dto.ts +24 -2
- package/src/authorize/oauth-exception.filter.ts +58 -0
- package/src/authorize/oauth-metadata.service.ts +67 -0
- package/src/authorize/well-known.controller.spec.ts +84 -0
- package/src/authorize/well-known.controller.ts +96 -0
- package/src/captcha/captcha.service.spec.ts +211 -4
- package/src/captcha/captcha.service.ts +127 -10
- package/src/core.module.ts +6 -0
- package/src/dashboard/dashboard-component-role/dashboard-component-role.service.ts +2 -3
- package/src/dashboard/dashboard-component-role/dashboard-component-role.sort.ts +18 -0
- package/src/dashboard/dashboard-item/dashboard-item.service.ts +2 -3
- package/src/dashboard/dashboard-item/dashboard-item.sort.ts +19 -0
- package/src/dashboard/dashboard-role/dashboard-role.service.ts +2 -3
- package/src/dashboard/dashboard-role/dashboard-role.sort.ts +16 -0
- package/src/file/file-upload-ticket.service.spec.ts +422 -0
- package/src/file/file-upload-ticket.service.ts +513 -0
- package/src/file/file.controller.ts +65 -0
- package/src/file/file.module.ts +14 -2
- package/src/file/file.service.spec.ts +94 -0
- package/src/file/file.service.ts +433 -53
- package/src/file/filename-encoding.util.spec.ts +54 -0
- package/src/file/filename-encoding.util.ts +67 -0
- package/src/file/image-optimization.service.spec.ts +156 -19
- package/src/file/image-optimization.service.ts +106 -21
- package/src/file/mcp-file-source.schema.ts +83 -0
- package/src/file/mcp-file-source.service.spec.ts +473 -0
- package/src/file/mcp-file-source.service.ts +445 -0
- package/src/file/mimetype-sniff.util.spec.ts +126 -0
- package/src/file/mimetype-sniff.util.ts +265 -0
- package/src/file/remote-fetch.util.spec.ts +117 -0
- package/src/file/remote-fetch.util.ts +330 -0
- package/src/impersonation/dto/start-impersonation.dto.ts +5 -2
- package/src/impersonation/impersonation.service.spec.ts +24 -1
- package/src/impersonation/impersonation.service.ts +14 -4
- package/src/incident/incident.controller.ts +9 -1
- package/src/incident/incident.service.ts +7 -0
- package/src/index.ts +17 -0
- package/src/integration-profile/integration-profile.secrets.ts +7 -0
- package/src/integration-profile/integration-profile.service.ts +7 -64
- package/src/integration-profile/integration-profile.utils.spec.ts +9 -0
- package/src/integration-profile/integration-profile.utils.ts +5 -5
- package/src/integration-profile/testers/index.ts +2 -0
- package/src/integration-profile/testers/remaining.testers.spec.ts +79 -0
- package/src/integration-profile/testers/shipping.testers.ts +94 -0
- package/src/mail/dto/send.dto.ts +6 -1
- package/src/mail/mail-template.spec.ts +58 -1
- package/src/mail/mail.service.spec.ts +112 -0
- package/src/mail/mail.service.ts +42 -4
- package/src/mail-sent/mail-sent.service.ts +3 -0
- package/src/mail-sent/mail-sent.sort.ts +19 -0
- package/src/mcp/filters/mcp-challenge.filter.spec.ts +76 -0
- package/src/mcp/filters/mcp-challenge.filter.ts +70 -0
- package/src/mcp/guards/mcp-auth.guard.spec.ts +51 -2
- package/src/mcp/guards/mcp-auth.guard.ts +24 -1
- package/src/mcp/guards/mcp-permission.guard.spec.ts +109 -15
- package/src/mcp/guards/mcp-permission.guard.ts +76 -51
- package/src/mcp/mcp-audit.service.spec.ts +107 -0
- package/src/mcp/mcp-audit.service.ts +37 -1
- package/src/mcp/mcp-permission.service.spec.ts +197 -0
- package/src/mcp/mcp-permission.service.ts +184 -0
- package/src/mcp/mcp-server.service.spec.ts +84 -2
- package/src/mcp/mcp-server.service.ts +111 -87
- package/src/mcp/mcp.controller.ts +8 -1
- package/src/mcp/mcp.module.ts +5 -1
- package/src/mcp/tools/core-ai.mcp-tools.ts +1 -1
- package/src/mcp/tools/core-dashboard-items.mcp-tools.ts +1 -1
- package/src/mcp/tools/core-dashboard.mcp-tools.ts +1 -1
- package/src/mcp/tools/core-event-webhooks.mcp-tools.ts +1 -2
- package/src/mcp/tools/core-files.mcp-tools.ts +202 -22
- package/src/mcp/tools/core-integration-events.mcp-tools.ts +1 -1
- package/src/mcp/tools/core-mail-sent.mcp-tools.ts +1 -1
- package/src/mcp/tools/core-mail.mcp-tools.ts +1 -1
- package/src/mcp/tools/core-menus.mcp-tools.ts +1 -2
- package/src/mcp/tools/core-notifications.mcp-tools.ts +0 -1
- package/src/mcp/tools/core-roles.mcp-tools.ts +1 -5
- package/src/mcp/tools/core-routes.mcp-tools.ts +1 -3
- package/src/mcp/tools/core-screens.mcp-tools.ts +1 -3
- package/src/mcp/tools/core-sessions.mcp-tools.ts +0 -2
- package/src/mcp/tools/core-settings.mcp-tools.ts +1 -2
- package/src/mcp/tools/core-users.mcp-tools.ts +1 -1
- package/src/mcp/tools/core-webhooks.mcp-tools.ts +1 -2
- package/src/mcp/types/mcp-context.type.ts +15 -6
- package/src/mcp-chat/mcp-chat.service.ts +8 -0
- package/src/meeting/meeting-provider.registry.ts +121 -0
- package/src/meeting/meeting.constants.ts +74 -0
- package/src/menu/menu.service.spec.ts +89 -0
- package/src/menu/menu.service.ts +29 -11
- package/src/message-template/dto/update-message-template.dto.ts +22 -0
- package/src/message-template/message-template-admin.service.spec.ts +163 -0
- package/src/message-template/message-template-admin.service.ts +239 -0
- package/src/message-template/message-template.controller.ts +52 -0
- package/src/message-template/message-template.module.ts +21 -0
- package/src/message-template/message-template.registry.spec.ts +62 -0
- package/src/message-template/message-template.registry.ts +125 -0
- package/src/message-template/message-template.service.spec.ts +149 -0
- package/src/message-template/message-template.service.ts +149 -0
- package/src/notification/notification.constants.ts +10 -0
- package/src/notification/notification.module.ts +3 -0
- package/src/notification/notification.service.spec.ts +75 -1
- package/src/notification/notification.service.ts +39 -1
- package/src/oauth/oauth.controller.ts +0 -1
- package/src/oauth/oauth.errors.ts +7 -0
- package/src/oauth/oauth.module.ts +6 -1
- package/src/oauth/oauth.service.spec.ts +58 -7
- package/src/oauth/oauth.service.ts +71 -32
- package/src/oauth/providers/abstract.provider.spec.ts +192 -0
- package/src/oauth/providers/abstract.provider.ts +46 -14
- package/src/oauth/providers/github.provider.ts +57 -34
- package/src/oauth/providers/microsoft-entra-id.provider.ts +8 -1
- package/src/observability/monitored-cron.spec.ts +164 -31
- package/src/observability/monitored-cron.ts +154 -62
- package/src/profile/dto/change-password.dto.ts +25 -7
- package/src/profile/profile.service.spec.ts +97 -0
- package/src/profile/profile.service.ts +9 -3
- package/src/realtime/realtime-tick.coalescer.spec.ts +92 -0
- package/src/realtime/realtime-tick.coalescer.ts +60 -0
- package/src/realtime/realtime.service.spec.ts +33 -0
- package/src/realtime/realtime.service.ts +28 -2
- package/src/redis/redis-kv.service.ts +261 -0
- package/src/redis/redis-master.spec.ts +111 -0
- package/src/redis/redis-master.ts +86 -0
- package/src/redis/redis-pubsub.ts +27 -3
- package/src/redis/redis.module.ts +17 -0
- package/src/session/session.service.spec.ts +121 -42
- package/src/session/session.service.ts +279 -82
- package/src/setting/setting.service.ts +44 -3
- package/src/task/task.service.spec.ts +41 -0
- package/src/task/task.service.ts +53 -13
- package/src/token/token.service.spec.ts +90 -0
- package/src/token/token.service.ts +48 -2
- package/src/user/user-credential.util.ts +14 -5
- package/src/user/user.service.ts +2 -12
- package/src/user/user.sort.ts +25 -0
- package/src/utils/abbreviate-name.spec.ts +50 -0
- package/src/utils/abbreviate-name.ts +44 -0
- package/src/utils/date-range-query.dto.ts +56 -0
- package/src/utils/date-range.spec.ts +175 -0
- package/src/utils/date-range.ts +246 -0
- package/src/webhook-integration/webhook-utils.spec.ts +67 -0
- package/src/webhook-integration/webhook-utils.ts +26 -0
- package/src/whatsapp-sender/whatsapp-recipients.spec.ts +89 -0
- package/src/whatsapp-sender/whatsapp-recipients.ts +83 -0
- package/src/whatsapp-sender/whatsapp-sender.service.spec.ts +112 -0
- package/src/whatsapp-sender/whatsapp-sender.service.ts +58 -5
- package/hedhog/query/device_pairing_request.sql +0 -46
- package/hedhog/query/impersonation_grant.sql +0 -71
- package/hedhog/query/oauth_authorize_pkce.sql +0 -58
- package/hedhog/query/oauth_mobile_state_token.sql +0 -30
- package/hedhog/query/oauth_web_ref_token.sql +0 -25
|
@@ -14,22 +14,62 @@ export declare class SessionService {
|
|
|
14
14
|
private readonly user;
|
|
15
15
|
private readonly paginationService;
|
|
16
16
|
private readonly logger;
|
|
17
|
+
/**
|
|
18
|
+
* Janela em que o refresh token ANTERIOR continua sendo aceito depois de uma
|
|
19
|
+
* rotacao.
|
|
20
|
+
*
|
|
21
|
+
* Sem ela a rotacao e um handoff sem tolerancia: se a resposta do
|
|
22
|
+
* `/auth/refresh` se perde no caminho de volta — rede caindo, aba suspensa,
|
|
23
|
+
* pod reciclado no meio de um rollout — o servidor ja girou e o cliente fica
|
|
24
|
+
* com um token que nao vale mais. Uma sessao valida de 30 dias morre por um
|
|
25
|
+
* soluco de rede, e o unico rastro e um 401 solto. A janela tambem absorve
|
|
26
|
+
* duas abas renovando ao mesmo tempo.
|
|
27
|
+
*
|
|
28
|
+
* Curta de proposito: ela existe para cobrir a corrida e a resposta perdida,
|
|
29
|
+
* nao para prolongar a vida do token.
|
|
30
|
+
*/
|
|
31
|
+
private static readonly REFRESH_GRACE_MS;
|
|
32
|
+
/**
|
|
33
|
+
* Sessao aberta por um cliente OAuth externo vive menos que a de login: o
|
|
34
|
+
* refresh token esta com um terceiro. Vale na criacao E na renovacao — sem
|
|
35
|
+
* reaplicar aqui, a primeira renovacao devolvia a sessao aos 30 dias globais e
|
|
36
|
+
* o limite virava decoracao.
|
|
37
|
+
*/
|
|
38
|
+
static readonly CONNECTOR_SESSION_TTL_MINUTES: number;
|
|
39
|
+
/**
|
|
40
|
+
* Tentativas de rotacao por chamada. A segunda cobre o caso de outra
|
|
41
|
+
* requisicao ter girado a sessao entre a leitura e o compare-and-swap.
|
|
42
|
+
*/
|
|
43
|
+
private static readonly REFRESH_ROTATION_ATTEMPTS;
|
|
17
44
|
constructor(prisma: PrismaService, security: SecurityService, setting: SettingService, token: TokenService, http: HttpService, user: UserService, paginationService: PaginationService);
|
|
18
|
-
|
|
45
|
+
/**
|
|
46
|
+
* `oauth` marca a sessao como pertencente a um cliente OAuth externo. Vem
|
|
47
|
+
* preenchido so pela troca do authorization code quando o cliente pediu um
|
|
48
|
+
* `resource`; o login normal continua chamando sem ele e nada muda.
|
|
49
|
+
*/
|
|
50
|
+
create(locale: string, userId: number, ipAddress: string, userAgent: string, browserId?: string | null, oauth?: {
|
|
51
|
+
audience: string;
|
|
52
|
+
clientId: string;
|
|
53
|
+
ttlMinutes?: number;
|
|
54
|
+
}): Promise<{
|
|
19
55
|
token: string;
|
|
20
56
|
session: {
|
|
21
57
|
id: number;
|
|
22
58
|
user_id: number;
|
|
23
59
|
user_agent: string;
|
|
24
|
-
impersonator_user_id: number | null;
|
|
25
60
|
created_at: Date;
|
|
61
|
+
impersonator_user_id: number | null;
|
|
26
62
|
updated_at: Date;
|
|
27
63
|
hash: string;
|
|
28
|
-
browser_id: string | null;
|
|
29
64
|
ip_address: string;
|
|
30
65
|
revoked_at: Date | null;
|
|
31
66
|
expires_at: Date;
|
|
67
|
+
browser_id: string | null;
|
|
32
68
|
impersonation_reason: string | null;
|
|
69
|
+
previous_hash: string | null;
|
|
70
|
+
previous_hash_expires_at: Date | null;
|
|
71
|
+
audience: string | null;
|
|
72
|
+
oauth_client_id: string | null;
|
|
33
73
|
};
|
|
34
74
|
}>;
|
|
35
75
|
/**
|
|
@@ -48,15 +88,19 @@ export declare class SessionService {
|
|
|
48
88
|
id: number;
|
|
49
89
|
user_id: number;
|
|
50
90
|
user_agent: string;
|
|
51
|
-
impersonator_user_id: number | null;
|
|
52
91
|
created_at: Date;
|
|
92
|
+
impersonator_user_id: number | null;
|
|
53
93
|
updated_at: Date;
|
|
54
94
|
hash: string;
|
|
55
|
-
browser_id: string | null;
|
|
56
95
|
ip_address: string;
|
|
57
96
|
revoked_at: Date | null;
|
|
58
97
|
expires_at: Date;
|
|
98
|
+
browser_id: string | null;
|
|
59
99
|
impersonation_reason: string | null;
|
|
100
|
+
previous_hash: string | null;
|
|
101
|
+
previous_hash_expires_at: Date | null;
|
|
102
|
+
audience: string | null;
|
|
103
|
+
oauth_client_id: string | null;
|
|
60
104
|
}>;
|
|
61
105
|
/**
|
|
62
106
|
* Anexa `impersonator: { id, name }` as sessoes simuladas. Nao ha relacao
|
|
@@ -65,23 +109,75 @@ export declare class SessionService {
|
|
|
65
109
|
* `UserService.listUserChangeLogs` com os atores.
|
|
66
110
|
*/
|
|
67
111
|
private attachImpersonators;
|
|
112
|
+
/**
|
|
113
|
+
* Anexa `oauth_client: { client_id, name }` as sessoes abertas por um cliente
|
|
114
|
+
* OAuth externo. Mesmo padrao de `attachImpersonators`, e pela mesma razao:
|
|
115
|
+
* `oauth_client_id` e VARCHAR puro, sem FK.
|
|
116
|
+
*
|
|
117
|
+
* Existe porque sem o nome a tela de seguranca do usuario fica ILEGIVEL para
|
|
118
|
+
* esse caso: um conector fala com a API a partir da nuvem do fornecedor, entao
|
|
119
|
+
* a linha aparece com user agent que o ua-parser nao reconhece
|
|
120
|
+
* (`python-httpx/0.28.1`), sem dispositivo, e geolocalizada no pais dele. Para
|
|
121
|
+
* quem esta no Brasil, e indistinguivel de uma conta invadida.
|
|
122
|
+
*/
|
|
123
|
+
private attachOAuthClients;
|
|
124
|
+
/**
|
|
125
|
+
* Gira o refresh token PRESERVANDO a identidade da sessao.
|
|
126
|
+
*
|
|
127
|
+
* A versao anterior revogava a linha e criava outra, com `id` novo. Como
|
|
128
|
+
* `TokenService.verify` valida a sessao no banco a cada request e recusa
|
|
129
|
+
* qualquer access token cuja sessao esteja revogada, o instante do refresh
|
|
130
|
+
* matava todos os access tokens em voo — na hora, e nao no `exp`. Numa tela
|
|
131
|
+
* que dispara ~10 chamadas em paralelo isso virava reacao em cadeia: cada 401
|
|
132
|
+
* disparava outro refresh, cada refresh matava os tokens dos outros. Nos logs
|
|
133
|
+
* de producao de 24/08 foram doze sessoes giradas em 18 segundos para o mesmo
|
|
134
|
+
* usuario, terminando em `Refresh rejected` e logout.
|
|
135
|
+
*
|
|
136
|
+
* Com o `id` estavel, o access token que ja saiu continua valido ate o proprio
|
|
137
|
+
* `exp`, e a rotacao deixa de ser um evento observavel pelo resto do app. De
|
|
138
|
+
* quebra a tabela para de crescer uma linha a cada 15 minutos por dispositivo.
|
|
139
|
+
*/
|
|
68
140
|
refresh(locale: string, refreshToken: string, ipAddress: string, userAgent: string, browserId?: string | null): Promise<{
|
|
69
141
|
token: string;
|
|
70
142
|
session: {
|
|
71
143
|
id: number;
|
|
72
144
|
user_id: number;
|
|
73
|
-
user_agent: string;
|
|
74
|
-
impersonator_user_id: number | null;
|
|
75
|
-
created_at: Date;
|
|
76
|
-
updated_at: Date;
|
|
77
|
-
hash: string;
|
|
78
|
-
browser_id: string | null;
|
|
79
145
|
ip_address: string;
|
|
80
|
-
|
|
146
|
+
user_agent: string;
|
|
147
|
+
browser_id: string;
|
|
81
148
|
expires_at: Date;
|
|
82
|
-
|
|
149
|
+
audience: string;
|
|
150
|
+
oauth_client_id: string;
|
|
83
151
|
};
|
|
84
152
|
}>;
|
|
153
|
+
/**
|
|
154
|
+
* Casa o refresh token apresentado com a sessao: pelo hash corrente, ou pelo
|
|
155
|
+
* anterior enquanto a janela de graca estiver aberta.
|
|
156
|
+
*
|
|
157
|
+
* O limite pela janela e o que impede um token antigo qualquer de ressuscitar
|
|
158
|
+
* uma sessao — so o imediatamente anterior, e so por
|
|
159
|
+
* `REFRESH_GRACE_MS`, continua servindo.
|
|
160
|
+
*/
|
|
161
|
+
/**
|
|
162
|
+
* Le a sessao de um refresh token SEM rotacionar nada.
|
|
163
|
+
*
|
|
164
|
+
* Existe porque `refresh()` encontra e gira na mesma operacao: quem precisa
|
|
165
|
+
* decidir se aceita a renovacao (o client confere? o resource confere? a sessao
|
|
166
|
+
* e de conector?) so podia fazer isso depois do compare-and-swap ja commitado —
|
|
167
|
+
* e uma recusa deixava o cliente com um token morto em 30 segundos, sem nunca
|
|
168
|
+
* receber o substituto. Validar antes e a diferenca entre negar um pedido e
|
|
169
|
+
* destruir a sessao.
|
|
170
|
+
*/
|
|
171
|
+
peekByRefreshToken(refreshToken: string): Promise<{
|
|
172
|
+
id: number;
|
|
173
|
+
user_id: number;
|
|
174
|
+
impersonator_user_id: number;
|
|
175
|
+
revoked_at: Date;
|
|
176
|
+
expires_at: Date;
|
|
177
|
+
audience: string;
|
|
178
|
+
oauth_client_id: string;
|
|
179
|
+
}>;
|
|
180
|
+
private refreshTokenWhere;
|
|
85
181
|
deleteRefreshTokenCookie(res: any, refreshToken: string): Promise<{
|
|
86
182
|
count: number;
|
|
87
183
|
userId: number;
|
|
@@ -108,15 +204,19 @@ export declare class SessionService {
|
|
|
108
204
|
id: number;
|
|
109
205
|
user_id: number;
|
|
110
206
|
user_agent: string;
|
|
111
|
-
impersonator_user_id: number | null;
|
|
112
207
|
created_at: Date;
|
|
208
|
+
impersonator_user_id: number | null;
|
|
113
209
|
updated_at: Date;
|
|
114
210
|
hash: string;
|
|
115
|
-
browser_id: string | null;
|
|
116
211
|
ip_address: string;
|
|
117
212
|
revoked_at: Date | null;
|
|
118
213
|
expires_at: Date;
|
|
214
|
+
browser_id: string | null;
|
|
119
215
|
impersonation_reason: string | null;
|
|
216
|
+
previous_hash: string | null;
|
|
217
|
+
previous_hash_expires_at: Date | null;
|
|
218
|
+
audience: string | null;
|
|
219
|
+
oauth_client_id: string | null;
|
|
120
220
|
}>;
|
|
121
221
|
/**
|
|
122
222
|
* Enforce maximum concurrent sessions limit for a user.
|
|
@@ -128,6 +228,9 @@ export declare class SessionService {
|
|
|
128
228
|
* @param isRotation - When true, the calling flow is replacing an existing session (refresh),
|
|
129
229
|
* not adding a new one, so no extra slot needs to be freed.
|
|
130
230
|
* @param currentBrowserId - The browser_id of the session being created/refreshed.
|
|
231
|
+
* @param keepSessionId - Session that must survive: its whole browser slot is
|
|
232
|
+
* excluded from eviction. Passed by the refresh flow, so renewing a live
|
|
233
|
+
* session can never revoke the very session being renewed.
|
|
131
234
|
*/
|
|
132
235
|
private enforceSessionLimit;
|
|
133
236
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.service.d.ts","sourceRoot":"","sources":["../../src/session/session.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC3E,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAG5C,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAanD,qBACa,cAAc;
|
|
1
|
+
{"version":3,"file":"session.service.d.ts","sourceRoot":"","sources":["../../src/session/session.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC3E,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAG5C,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAanD,qBACa,cAAc;IAkCvB,OAAO,CAAC,MAAM;IAEd,OAAO,CAAC,QAAQ;IAEhB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAExB,OAAO,CAAC,QAAQ,CAAC,KAAK;IAEtB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAErB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAErB,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IA7CpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmC;IAE1D;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAU;IAElD;;;;;OAKG;IACH,MAAM,CAAC,QAAQ,CAAC,6BAA6B,SAAe;IAE5D;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,yBAAyB,CAAK;gBAG5C,MAAM,EAAE,aAAa,EAErB,QAAQ,EAAE,eAAe,EAEhB,OAAO,EAAE,cAAc,EAEvB,KAAK,EAAE,YAAY,EAEnB,IAAI,EAAE,WAAW,EAEjB,IAAI,EAAE,WAAW,EAEjB,iBAAiB,EAAE,iBAAiB;IAGvD;;;;OAIG;IACG,MAAM,CACV,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,EACzB,KAAK,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE;;;;;;;;;;;;;;;;;;;;;IAwDrE;;;;;;;;;;;OAWG;IACG,mBAAmB,CACvB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,kBAAkB,EAAE,MAAM,EAC1B,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,MAAM,EACxB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;;;;;;;;;;;;;;;;;;IAsBxB;;;;;OAKG;YACW,mBAAmB;IAwBjC;;;;;;;;;;OAUG;YACW,kBAAkB;IAgChC;;;;;;;;;;;;;;;OAeG;IACG,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;;;;;;;;;;;;;IAiHnH;;;;;;;OAOG;IACH;;;;;;;;;OASG;IACG,kBAAkB,CAAC,YAAY,EAAE,MAAM;;;;;;;;;IAmB7C,OAAO,CAAC,iBAAiB;IASnB,wBAAwB,CAAC,GAAG,KAAA,EAAE,YAAY,EAAE,MAAM;;;;IAiClD,eAAe,CAAC,gBAAgB,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;IAmD/E,qBAAqB,CAAC,gBAAgB,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;YAmD7E,YAAY;YAmBZ,mBAAmB;IAQ3B,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;IAexD,iBAAiB,CAAC,MAAM,EAAE,MAAM;IAIhC,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;IA0BzE;;;;;;;;;;;;;OAaG;YACW,mBAAmB;CAuElC"}
|
|
@@ -46,7 +46,13 @@ let SessionService = SessionService_1 = class SessionService {
|
|
|
46
46
|
this.paginationService = paginationService;
|
|
47
47
|
this.logger = new common_1.Logger(SessionService_1.name);
|
|
48
48
|
}
|
|
49
|
-
|
|
49
|
+
/**
|
|
50
|
+
* `oauth` marca a sessao como pertencente a um cliente OAuth externo. Vem
|
|
51
|
+
* preenchido so pela troca do authorization code quando o cliente pediu um
|
|
52
|
+
* `resource`; o login normal continua chamando sem ele e nada muda.
|
|
53
|
+
*/
|
|
54
|
+
async create(locale, userId, ipAddress, userAgent, browserId, oauth) {
|
|
55
|
+
var _a, _b;
|
|
50
56
|
if (!userId) {
|
|
51
57
|
throw new common_1.BadRequestException((0, api_locale_1.getLocaleText)('session.userIdRequired', locale, 'User ID is required to create a session.'));
|
|
52
58
|
}
|
|
@@ -63,17 +69,31 @@ let SessionService = SessionService_1 = class SessionService {
|
|
|
63
69
|
const token = await this.token.createOpaqueToken();
|
|
64
70
|
const hash = this.security.hashWithPepper(token);
|
|
65
71
|
const maxSessions = Number(settings['max-concurrent-sessions']) || 0;
|
|
66
|
-
|
|
72
|
+
// E pela mesma razao a sessao de conector nao DISPARA o despejo ao nascer.
|
|
73
|
+
// `createImpersonation` ja se isenta assim; aqui o efeito seria o usuario
|
|
74
|
+
// perder o celular por ter autorizado um app.
|
|
75
|
+
if (maxSessions > 0 && !oauth) {
|
|
67
76
|
await this.enforceSessionLimit(userId, maxSessions, false, browserId);
|
|
68
77
|
}
|
|
78
|
+
// Sessao de conector vive menos que a de login. Nao e simetria estetica: o
|
|
79
|
+
// refresh token esta com um terceiro, e 30 dias de janela para um segredo que
|
|
80
|
+
// saiu do nosso dominio e generoso demais.
|
|
81
|
+
//
|
|
82
|
+
// `||` e nao `??` em toda a cadeia: `Number(undefined)` e NaN, que passa
|
|
83
|
+
// direto por `??` e produziria um expires_at invalido.
|
|
84
|
+
const ttlMinutes = (oauth === null || oauth === void 0 ? void 0 : oauth.ttlMinutes) ||
|
|
85
|
+
Number(settings['refresh-token-expiration-minutes']) ||
|
|
86
|
+
43200;
|
|
69
87
|
const session = await this.prisma.user_session.create({
|
|
70
88
|
data: {
|
|
71
89
|
user_id: userId,
|
|
72
90
|
ip_address: ipAddress,
|
|
73
91
|
user_agent: userAgent,
|
|
74
|
-
expires_at: new Date(Date.now() +
|
|
92
|
+
expires_at: new Date(Date.now() + ttlMinutes * 60 * 1000),
|
|
75
93
|
hash,
|
|
76
94
|
browser_id: browserId !== null && browserId !== void 0 ? browserId : null,
|
|
95
|
+
audience: (_a = oauth === null || oauth === void 0 ? void 0 : oauth.audience) !== null && _a !== void 0 ? _a : null,
|
|
96
|
+
oauth_client_id: (_b = oauth === null || oauth === void 0 ? void 0 : oauth.clientId) !== null && _b !== void 0 ? _b : null,
|
|
77
97
|
},
|
|
78
98
|
});
|
|
79
99
|
return { token, session };
|
|
@@ -131,88 +151,197 @@ let SessionService = SessionService_1 = class SessionService {
|
|
|
131
151
|
: null }));
|
|
132
152
|
});
|
|
133
153
|
}
|
|
154
|
+
/**
|
|
155
|
+
* Anexa `oauth_client: { client_id, name }` as sessoes abertas por um cliente
|
|
156
|
+
* OAuth externo. Mesmo padrao de `attachImpersonators`, e pela mesma razao:
|
|
157
|
+
* `oauth_client_id` e VARCHAR puro, sem FK.
|
|
158
|
+
*
|
|
159
|
+
* Existe porque sem o nome a tela de seguranca do usuario fica ILEGIVEL para
|
|
160
|
+
* esse caso: um conector fala com a API a partir da nuvem do fornecedor, entao
|
|
161
|
+
* a linha aparece com user agent que o ua-parser nao reconhece
|
|
162
|
+
* (`python-httpx/0.28.1`), sem dispositivo, e geolocalizada no pais dele. Para
|
|
163
|
+
* quem esta no Brasil, e indistinguivel de uma conta invadida.
|
|
164
|
+
*/
|
|
165
|
+
async attachOAuthClients(rows) {
|
|
166
|
+
const ids = Array.from(new Set(rows
|
|
167
|
+
.map((row) => row.oauth_client_id)
|
|
168
|
+
.filter((id) => typeof id === 'string' && id.length > 0)));
|
|
169
|
+
if (!ids.length) {
|
|
170
|
+
return rows.map((row) => (Object.assign(Object.assign({}, row), { oauth_client: null })));
|
|
171
|
+
}
|
|
172
|
+
const clients = await this.prisma.oauth_app_client.findMany({
|
|
173
|
+
where: { client_id: { in: ids } },
|
|
174
|
+
select: { client_id: true, name: true },
|
|
175
|
+
});
|
|
176
|
+
const byId = new Map(clients.map((client) => [client.client_id, client]));
|
|
177
|
+
return rows.map((row) => {
|
|
178
|
+
var _a;
|
|
179
|
+
return (Object.assign(Object.assign({}, row), { oauth_client: row.oauth_client_id
|
|
180
|
+
? // Client removido do registro ainda tem sessao viva: cai no proprio
|
|
181
|
+
// slug, que e melhor que sumir com a informacao.
|
|
182
|
+
((_a = byId.get(row.oauth_client_id)) !== null && _a !== void 0 ? _a : {
|
|
183
|
+
client_id: row.oauth_client_id,
|
|
184
|
+
name: row.oauth_client_id,
|
|
185
|
+
})
|
|
186
|
+
: null }));
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Gira o refresh token PRESERVANDO a identidade da sessao.
|
|
191
|
+
*
|
|
192
|
+
* A versao anterior revogava a linha e criava outra, com `id` novo. Como
|
|
193
|
+
* `TokenService.verify` valida a sessao no banco a cada request e recusa
|
|
194
|
+
* qualquer access token cuja sessao esteja revogada, o instante do refresh
|
|
195
|
+
* matava todos os access tokens em voo — na hora, e nao no `exp`. Numa tela
|
|
196
|
+
* que dispara ~10 chamadas em paralelo isso virava reacao em cadeia: cada 401
|
|
197
|
+
* disparava outro refresh, cada refresh matava os tokens dos outros. Nos logs
|
|
198
|
+
* de producao de 24/08 foram doze sessoes giradas em 18 segundos para o mesmo
|
|
199
|
+
* usuario, terminando em `Refresh rejected` e logout.
|
|
200
|
+
*
|
|
201
|
+
* Com o `id` estavel, o access token que ja saiu continua valido ate o proprio
|
|
202
|
+
* `exp`, e a rotacao deixa de ser um evento observavel pelo resto do app. De
|
|
203
|
+
* quebra a tabela para de crescer uma linha a cada 15 minutos por dispositivo.
|
|
204
|
+
*/
|
|
134
205
|
async refresh(locale, refreshToken, ipAddress, userAgent, browserId) {
|
|
206
|
+
var _a, _b, _c, _d, _e, _f;
|
|
135
207
|
if (!refreshToken) {
|
|
136
208
|
throw new common_1.UnauthorizedException((0, api_locale_1.getLocaleText)('accessDenied', locale, 'Access denied.'));
|
|
137
209
|
}
|
|
138
210
|
const hash = this.security.hashWithPepper(refreshToken);
|
|
139
|
-
const rawSession = await this.prisma.user_session.findFirst({
|
|
140
|
-
where: { hash },
|
|
141
|
-
select: { id: true, user_id: true, ip_address: true, user_agent: true, revoked_at: true, expires_at: true, browser_id: true, impersonator_user_id: true },
|
|
142
|
-
});
|
|
143
|
-
if (!rawSession) {
|
|
144
|
-
this.logger.warn(`[SessionRefresh] Refresh rejected: hash prefix ${hash.slice(0, 8)} not found in DB`);
|
|
145
|
-
throw new common_1.UnauthorizedException((0, api_locale_1.getLocaleText)('accessDenied', locale, 'Access denied.'));
|
|
146
|
-
}
|
|
147
|
-
if (rawSession.revoked_at !== null) {
|
|
148
|
-
this.logger.warn(`[SessionRefresh] Refresh rejected: sessionId=${rawSession.id} userId=${rawSession.user_id} revoked_at=${rawSession.revoked_at.toISOString()} hash prefix ${hash.slice(0, 8)}`);
|
|
149
|
-
throw new common_1.UnauthorizedException((0, api_locale_1.getLocaleText)('accessDenied', locale, 'Access denied.'));
|
|
150
|
-
}
|
|
151
|
-
if (rawSession.expires_at <= new Date()) {
|
|
152
|
-
this.logger.warn(`[SessionRefresh] Refresh rejected: sessionId=${rawSession.id} userId=${rawSession.user_id} expired expires_at=${rawSession.expires_at.toISOString()} hash prefix ${hash.slice(0, 8)}`);
|
|
153
|
-
throw new common_1.UnauthorizedException((0, api_locale_1.getLocaleText)('accessDenied', locale, 'Access denied.'));
|
|
154
|
-
}
|
|
155
|
-
// Simulacao de acesso e nao renovavel. `createImpersonation` ja descarta o
|
|
156
|
-
// token opaco, entao chegar aqui significa que alguem obteve o segredo por
|
|
157
|
-
// outro caminho — e renovar transformaria a janela de 30 minutos numa sessao
|
|
158
|
-
// normal de 30 dias, sem a marca de simulado.
|
|
159
|
-
if (rawSession.impersonator_user_id !== null) {
|
|
160
|
-
this.logger.warn(`[SessionRefresh] Refresh rejected: sessionId=${rawSession.id} userId=${rawSession.user_id} is an impersonation session`);
|
|
161
|
-
throw new common_1.UnauthorizedException((0, api_locale_1.getLocaleText)('accessDenied', locale, 'Access denied.'));
|
|
162
|
-
}
|
|
163
|
-
const session = {
|
|
164
|
-
id: rawSession.id,
|
|
165
|
-
user_id: rawSession.user_id,
|
|
166
|
-
ip_address: rawSession.ip_address,
|
|
167
|
-
user_agent: rawSession.user_agent,
|
|
168
|
-
browser_id: rawSession.browser_id,
|
|
169
|
-
};
|
|
170
211
|
const settings = await this.setting.getSettingValues([
|
|
171
212
|
'refresh-token-expiration-minutes',
|
|
172
213
|
'max-concurrent-sessions',
|
|
173
214
|
]);
|
|
174
215
|
const maxSessions = Number(settings['max-concurrent-sessions']) || 0;
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
216
|
+
const ttlMinutes = Number(settings['refresh-token-expiration-minutes']) || 43200;
|
|
217
|
+
for (let attempt = 1; attempt <= SessionService_1.REFRESH_ROTATION_ATTEMPTS; attempt++) {
|
|
218
|
+
const rawSession = await this.prisma.user_session.findFirst({
|
|
219
|
+
where: this.refreshTokenWhere(hash),
|
|
220
|
+
select: { id: true, user_id: true, ip_address: true, user_agent: true, revoked_at: true, expires_at: true, browser_id: true, impersonator_user_id: true, hash: true, audience: true, oauth_client_id: true },
|
|
221
|
+
});
|
|
222
|
+
if (!rawSession) {
|
|
223
|
+
this.logger.warn(`[SessionRefresh] Refresh rejected: hash prefix ${hash.slice(0, 8)} not found in DB`);
|
|
224
|
+
throw new common_1.UnauthorizedException((0, api_locale_1.getLocaleText)('accessDenied', locale, 'Access denied.'));
|
|
225
|
+
}
|
|
226
|
+
if (rawSession.revoked_at !== null) {
|
|
227
|
+
this.logger.warn(`[SessionRefresh] Refresh rejected: sessionId=${rawSession.id} userId=${rawSession.user_id} revoked_at=${rawSession.revoked_at.toISOString()} hash prefix ${hash.slice(0, 8)}`);
|
|
228
|
+
throw new common_1.UnauthorizedException((0, api_locale_1.getLocaleText)('accessDenied', locale, 'Access denied.'));
|
|
229
|
+
}
|
|
230
|
+
if (rawSession.expires_at <= new Date()) {
|
|
231
|
+
this.logger.warn(`[SessionRefresh] Refresh rejected: sessionId=${rawSession.id} userId=${rawSession.user_id} expired expires_at=${rawSession.expires_at.toISOString()} hash prefix ${hash.slice(0, 8)}`);
|
|
232
|
+
throw new common_1.UnauthorizedException((0, api_locale_1.getLocaleText)('accessDenied', locale, 'Access denied.'));
|
|
233
|
+
}
|
|
234
|
+
// Simulacao de acesso e nao renovavel. `createImpersonation` ja descarta o
|
|
235
|
+
// token opaco, entao chegar aqui significa que alguem obteve o segredo por
|
|
236
|
+
// outro caminho — e renovar transformaria a janela de 30 minutos numa sessao
|
|
237
|
+
// normal de 30 dias, sem a marca de simulado.
|
|
238
|
+
if (rawSession.impersonator_user_id !== null) {
|
|
239
|
+
this.logger.warn(`[SessionRefresh] Refresh rejected: sessionId=${rawSession.id} userId=${rawSession.user_id} is an impersonation session`);
|
|
240
|
+
throw new common_1.UnauthorizedException((0, api_locale_1.getLocaleText)('accessDenied', locale, 'Access denied.'));
|
|
241
|
+
}
|
|
242
|
+
const nextRefreshToken = await this.token.createOpaqueToken();
|
|
243
|
+
const nextHash = this.security.hashWithPepper(nextRefreshToken);
|
|
244
|
+
const now = Date.now();
|
|
245
|
+
// Sessao de conector nao volta aos 30 dias globais so por ter sido
|
|
246
|
+
// renovada — o TTL curto e a propriedade que a distingue.
|
|
247
|
+
const effectiveTtl = rawSession.audience
|
|
248
|
+
? Math.min(ttlMinutes, SessionService_1.CONNECTOR_SESSION_TTL_MINUTES)
|
|
249
|
+
: ttlMinutes;
|
|
250
|
+
const expiresAt = new Date(now + effectiveTtl * 60 * 1000);
|
|
251
|
+
const nextIpAddress = (ipAddress === null || ipAddress === void 0 ? void 0 : ipAddress.trim()) || rawSession.ip_address || 'unknown';
|
|
252
|
+
const nextUserAgent = (userAgent === null || userAgent === void 0 ? void 0 : userAgent.trim()) || rawSession.user_agent || 'unknown';
|
|
253
|
+
// Compare-and-swap pelo hash corrente, na MESMA linha. Se outra requisicao
|
|
254
|
+
// girou primeiro, `count` volta 0 e a proxima volta le o hash novo — em vez
|
|
255
|
+
// de devolver 401 para o perdedor da corrida, como antes.
|
|
256
|
+
const updateResult = await this.prisma.user_session.updateMany({
|
|
188
257
|
where: {
|
|
189
|
-
id:
|
|
258
|
+
id: rawSession.id,
|
|
190
259
|
revoked_at: null,
|
|
260
|
+
hash: rawSession.hash,
|
|
191
261
|
},
|
|
192
262
|
data: {
|
|
193
|
-
|
|
263
|
+
hash: nextHash,
|
|
264
|
+
previous_hash: rawSession.hash,
|
|
265
|
+
previous_hash_expires_at: new Date(now + SessionService_1.REFRESH_GRACE_MS),
|
|
266
|
+
expires_at: expiresAt,
|
|
267
|
+
ip_address: nextIpAddress,
|
|
268
|
+
user_agent: nextUserAgent,
|
|
269
|
+
browser_id: (_b = (_a = rawSession.browser_id) !== null && _a !== void 0 ? _a : browserId) !== null && _b !== void 0 ? _b : null,
|
|
270
|
+
updated_at: new Date(),
|
|
194
271
|
},
|
|
195
272
|
});
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
273
|
+
if (updateResult.count === 0) {
|
|
274
|
+
this.logger.debug(`[SessionRefresh] Rotation lost the CAS sessionId=${rawSession.id} userId=${rawSession.user_id} attempt=${attempt}`);
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
// Depois da rotacao, nunca antes. Rodando antes, o limite podia revogar a
|
|
278
|
+
// propria sessao que estava sendo renovada e o CAS falhava sozinho. E
|
|
279
|
+
// `keepSessionId` garante que renovar nunca despeja quem esta renovando.
|
|
280
|
+
if (maxSessions > 0) {
|
|
281
|
+
await this.enforceSessionLimit(rawSession.user_id, maxSessions, true, browserId, rawSession.id);
|
|
282
|
+
}
|
|
283
|
+
this.logger.debug(`[SessionRefresh] Rotation successful userId=${rawSession.user_id} sessionId=${rawSession.id} durationMs=${Date.now() - now}`);
|
|
284
|
+
return {
|
|
285
|
+
token: nextRefreshToken,
|
|
286
|
+
session: {
|
|
287
|
+
id: rawSession.id,
|
|
288
|
+
user_id: rawSession.user_id,
|
|
199
289
|
ip_address: nextIpAddress,
|
|
200
290
|
user_agent: nextUserAgent,
|
|
291
|
+
browser_id: (_d = (_c = rawSession.browser_id) !== null && _c !== void 0 ? _c : browserId) !== null && _d !== void 0 ? _d : null,
|
|
201
292
|
expires_at: expiresAt,
|
|
202
|
-
|
|
203
|
-
|
|
293
|
+
// Quem renova precisa saber que esta renovando uma sessao de conector:
|
|
294
|
+
// e dai que sai o `aud` do proximo access token. Sem isso a rotacao
|
|
295
|
+
// devolveria um token de sessao comum e desfaria o vinculo.
|
|
296
|
+
audience: (_e = rawSession.audience) !== null && _e !== void 0 ? _e : null,
|
|
297
|
+
oauth_client_id: (_f = rawSession.oauth_client_id) !== null && _f !== void 0 ? _f : null,
|
|
204
298
|
},
|
|
205
|
-
}
|
|
206
|
-
return { updateResult, newSession };
|
|
207
|
-
});
|
|
208
|
-
if (updateResult.count === 0) {
|
|
209
|
-
this.logger.warn(`[SessionRefresh] Race condition detected: userId=${session.user_id} oldSessionId=${session.id} already rotated by another request (durationMs=${Date.now() - txStart})`);
|
|
210
|
-
throw new common_1.UnauthorizedException((0, api_locale_1.getLocaleText)('accessDenied', locale, 'Access denied.'));
|
|
299
|
+
};
|
|
211
300
|
}
|
|
212
|
-
this.logger.
|
|
301
|
+
this.logger.warn(`[SessionRefresh] Rotation gave up after ${SessionService_1.REFRESH_ROTATION_ATTEMPTS} attempts: hash prefix ${hash.slice(0, 8)}`);
|
|
302
|
+
throw new common_1.UnauthorizedException((0, api_locale_1.getLocaleText)('accessDenied', locale, 'Access denied.'));
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Casa o refresh token apresentado com a sessao: pelo hash corrente, ou pelo
|
|
306
|
+
* anterior enquanto a janela de graca estiver aberta.
|
|
307
|
+
*
|
|
308
|
+
* O limite pela janela e o que impede um token antigo qualquer de ressuscitar
|
|
309
|
+
* uma sessao — so o imediatamente anterior, e so por
|
|
310
|
+
* `REFRESH_GRACE_MS`, continua servindo.
|
|
311
|
+
*/
|
|
312
|
+
/**
|
|
313
|
+
* Le a sessao de um refresh token SEM rotacionar nada.
|
|
314
|
+
*
|
|
315
|
+
* Existe porque `refresh()` encontra e gira na mesma operacao: quem precisa
|
|
316
|
+
* decidir se aceita a renovacao (o client confere? o resource confere? a sessao
|
|
317
|
+
* e de conector?) so podia fazer isso depois do compare-and-swap ja commitado —
|
|
318
|
+
* e uma recusa deixava o cliente com um token morto em 30 segundos, sem nunca
|
|
319
|
+
* receber o substituto. Validar antes e a diferenca entre negar um pedido e
|
|
320
|
+
* destruir a sessao.
|
|
321
|
+
*/
|
|
322
|
+
async peekByRefreshToken(refreshToken) {
|
|
323
|
+
if (!refreshToken)
|
|
324
|
+
return null;
|
|
325
|
+
const hash = this.security.hashWithPepper(refreshToken);
|
|
326
|
+
return this.prisma.user_session.findFirst({
|
|
327
|
+
where: this.refreshTokenWhere(hash),
|
|
328
|
+
select: {
|
|
329
|
+
id: true,
|
|
330
|
+
user_id: true,
|
|
331
|
+
revoked_at: true,
|
|
332
|
+
expires_at: true,
|
|
333
|
+
audience: true,
|
|
334
|
+
oauth_client_id: true,
|
|
335
|
+
impersonator_user_id: true,
|
|
336
|
+
},
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
refreshTokenWhere(hash) {
|
|
213
340
|
return {
|
|
214
|
-
|
|
215
|
-
|
|
341
|
+
OR: [
|
|
342
|
+
{ hash },
|
|
343
|
+
{ previous_hash: hash, previous_hash_expires_at: { gt: new Date() } },
|
|
344
|
+
],
|
|
216
345
|
};
|
|
217
346
|
}
|
|
218
347
|
async deleteRefreshTokenCookie(res, refreshToken) {
|
|
@@ -222,20 +351,18 @@ let SessionService = SessionService_1 = class SessionService {
|
|
|
222
351
|
return { count: 0, userId: null };
|
|
223
352
|
}
|
|
224
353
|
const hash = this.security.hashWithPepper(refreshToken);
|
|
354
|
+
// Aceita tambem o hash anterior dentro da janela de graca: sair da conta com
|
|
355
|
+
// um token que acabou de girar tem de derrubar a sessao de verdade, e nao
|
|
356
|
+
// devolver sucesso deixando-a viva.
|
|
357
|
+
const where = Object.assign({ revoked_at: null }, this.refreshTokenWhere(hash));
|
|
225
358
|
const session = await this.prisma.user_session.findFirst({
|
|
226
|
-
where
|
|
227
|
-
revoked_at: null,
|
|
228
|
-
hash,
|
|
229
|
-
},
|
|
359
|
+
where,
|
|
230
360
|
select: {
|
|
231
361
|
user_id: true,
|
|
232
362
|
},
|
|
233
363
|
});
|
|
234
364
|
const result = await this.prisma.user_session.updateMany({
|
|
235
|
-
where
|
|
236
|
-
revoked_at: null,
|
|
237
|
-
hash,
|
|
238
|
-
},
|
|
365
|
+
where,
|
|
239
366
|
data: {
|
|
240
367
|
revoked_at: new Date(),
|
|
241
368
|
},
|
|
@@ -277,7 +404,7 @@ let SessionService = SessionService_1 = class SessionService {
|
|
|
277
404
|
return Object.assign(Object.assign({}, s), { location });
|
|
278
405
|
}));
|
|
279
406
|
return {
|
|
280
|
-
data: await this.attachImpersonators(itemsWithLocation),
|
|
407
|
+
data: await this.attachOAuthClients(await this.attachImpersonators(itemsWithLocation)),
|
|
281
408
|
total: paginate.total || 0,
|
|
282
409
|
lastPage: Math.ceil((paginate.total || 0) / (paginate.pageSize || 1)),
|
|
283
410
|
page: (_a = paginate.page) !== null && _a !== void 0 ? _a : 1,
|
|
@@ -320,7 +447,7 @@ let SessionService = SessionService_1 = class SessionService {
|
|
|
320
447
|
return Object.assign(Object.assign({}, s), { location });
|
|
321
448
|
}));
|
|
322
449
|
return {
|
|
323
|
-
data: await this.attachImpersonators(itemsWithLocation),
|
|
450
|
+
data: await this.attachOAuthClients(await this.attachImpersonators(itemsWithLocation)),
|
|
324
451
|
total: paginate.total || 0,
|
|
325
452
|
lastPage: Math.ceil((paginate.total || 0) / (paginate.pageSize || 1)),
|
|
326
453
|
page: (_a = paginate.page) !== null && _a !== void 0 ? _a : 1,
|
|
@@ -404,8 +531,11 @@ let SessionService = SessionService_1 = class SessionService {
|
|
|
404
531
|
* @param isRotation - When true, the calling flow is replacing an existing session (refresh),
|
|
405
532
|
* not adding a new one, so no extra slot needs to be freed.
|
|
406
533
|
* @param currentBrowserId - The browser_id of the session being created/refreshed.
|
|
534
|
+
* @param keepSessionId - Session that must survive: its whole browser slot is
|
|
535
|
+
* excluded from eviction. Passed by the refresh flow, so renewing a live
|
|
536
|
+
* session can never revoke the very session being renewed.
|
|
407
537
|
*/
|
|
408
|
-
async enforceSessionLimit(userId, maxSessions, isRotation = false, currentBrowserId) {
|
|
538
|
+
async enforceSessionLimit(userId, maxSessions, isRotation = false, currentBrowserId, keepSessionId) {
|
|
409
539
|
var _a;
|
|
410
540
|
const activeSessions = await this.prisma.user_session.findMany({
|
|
411
541
|
where: {
|
|
@@ -415,6 +545,11 @@ let SessionService = SessionService_1 = class SessionService {
|
|
|
415
545
|
// Simulacao de acesso nao consome slot: ela nasce com browser_id nulo e
|
|
416
546
|
// seria contada como slot proprio, despejando uma sessao real do alvo.
|
|
417
547
|
impersonator_user_id: null,
|
|
548
|
+
// Sessao de cliente OAuth externo, pelo mesmo motivo: um cliente MCP nao
|
|
549
|
+
// manda X-Browser-ID, entao cada conexao viraria um slot proprio e
|
|
550
|
+
// derrubaria um dispositivo de verdade do usuario — que so queria ligar o
|
|
551
|
+
// Claude. Conector nao e navegador.
|
|
552
|
+
audience: null,
|
|
418
553
|
},
|
|
419
554
|
orderBy: { created_at: 'asc' },
|
|
420
555
|
select: { id: true, browser_id: true }
|
|
@@ -428,6 +563,15 @@ let SessionService = SessionService_1 = class SessionService {
|
|
|
428
563
|
}
|
|
429
564
|
const slots = Array.from(slotMap.values());
|
|
430
565
|
const currentSlotCount = slots.length;
|
|
566
|
+
// O slot de quem esta renovando nunca entra na fila de despejo: renovar uma
|
|
567
|
+
// sessao viva nao pode derrubar a propria sessao. Sem isto, um usuario com
|
|
568
|
+
// mais slots que o limite era deslogado no proximo refresh — a rotacao
|
|
569
|
+
// revogava a si mesma e o compare-and-swap logo em seguida nao achava a
|
|
570
|
+
// linha.
|
|
571
|
+
const protectedSlot = typeof keepSessionId === 'number'
|
|
572
|
+
? slots.find((slot) => slot.ids.includes(keepSessionId))
|
|
573
|
+
: undefined;
|
|
574
|
+
const evictable = slots.filter((slot) => slot !== protectedSlot);
|
|
431
575
|
// A new browser slot is created only when:
|
|
432
576
|
// - it's not a rotation (rotation keeps the same slot), AND
|
|
433
577
|
// - the current browser doesn't already have an active slot
|
|
@@ -437,10 +581,13 @@ let SessionService = SessionService_1 = class SessionService {
|
|
|
437
581
|
this.logger.debug(`[SessionLimit] userId=${userId} slots=${currentSlotCount} maxSessions=${maxSessions} isRotation=${isRotation} browserId=${currentBrowserId !== null && currentBrowserId !== void 0 ? currentBrowserId : 'null'} newSlot=${isNewSlot} toRevoke=${Math.max(0, slotsToRevoke)}`);
|
|
438
582
|
if (slotsToRevoke > 0) {
|
|
439
583
|
const sessionIdsToRevoke = [];
|
|
440
|
-
for (let i = 0; i < slotsToRevoke; i++) {
|
|
441
|
-
sessionIdsToRevoke.push(...
|
|
584
|
+
for (let i = 0; i < Math.min(slotsToRevoke, evictable.length); i++) {
|
|
585
|
+
sessionIdsToRevoke.push(...evictable[i].ids);
|
|
586
|
+
}
|
|
587
|
+
if (sessionIdsToRevoke.length === 0) {
|
|
588
|
+
return;
|
|
442
589
|
}
|
|
443
|
-
this.logger.warn(`[SessionLimit] Revoking ${
|
|
590
|
+
this.logger.warn(`[SessionLimit] Revoking ${sessionIdsToRevoke.length} session(s) in the oldest browser slot(s) for userId=${userId}: [${sessionIdsToRevoke.join(',')}]`);
|
|
444
591
|
await this.prisma.user_session.updateMany({
|
|
445
592
|
where: { id: { in: sessionIdsToRevoke } },
|
|
446
593
|
data: { revoked_at: new Date() }
|
|
@@ -449,6 +596,33 @@ let SessionService = SessionService_1 = class SessionService {
|
|
|
449
596
|
}
|
|
450
597
|
};
|
|
451
598
|
exports.SessionService = SessionService;
|
|
599
|
+
/**
|
|
600
|
+
* Janela em que o refresh token ANTERIOR continua sendo aceito depois de uma
|
|
601
|
+
* rotacao.
|
|
602
|
+
*
|
|
603
|
+
* Sem ela a rotacao e um handoff sem tolerancia: se a resposta do
|
|
604
|
+
* `/auth/refresh` se perde no caminho de volta — rede caindo, aba suspensa,
|
|
605
|
+
* pod reciclado no meio de um rollout — o servidor ja girou e o cliente fica
|
|
606
|
+
* com um token que nao vale mais. Uma sessao valida de 30 dias morre por um
|
|
607
|
+
* soluco de rede, e o unico rastro e um 401 solto. A janela tambem absorve
|
|
608
|
+
* duas abas renovando ao mesmo tempo.
|
|
609
|
+
*
|
|
610
|
+
* Curta de proposito: ela existe para cobrir a corrida e a resposta perdida,
|
|
611
|
+
* nao para prolongar a vida do token.
|
|
612
|
+
*/
|
|
613
|
+
SessionService.REFRESH_GRACE_MS = 30000;
|
|
614
|
+
/**
|
|
615
|
+
* Sessao aberta por um cliente OAuth externo vive menos que a de login: o
|
|
616
|
+
* refresh token esta com um terceiro. Vale na criacao E na renovacao — sem
|
|
617
|
+
* reaplicar aqui, a primeira renovacao devolvia a sessao aos 30 dias globais e
|
|
618
|
+
* o limite virava decoracao.
|
|
619
|
+
*/
|
|
620
|
+
SessionService.CONNECTOR_SESSION_TTL_MINUTES = 7 * 24 * 60;
|
|
621
|
+
/**
|
|
622
|
+
* Tentativas de rotacao por chamada. A segunda cobre o caso de outra
|
|
623
|
+
* requisicao ter girado a sessao entre a leitura e o compare-and-swap.
|
|
624
|
+
*/
|
|
625
|
+
SessionService.REFRESH_ROTATION_ATTEMPTS = 2;
|
|
452
626
|
exports.SessionService = SessionService = SessionService_1 = __decorate([
|
|
453
627
|
(0, common_1.Injectable)(),
|
|
454
628
|
__param(1, (0, common_1.Inject)((0, common_1.forwardRef)(() => security_service_1.SecurityService))),
|