@solidxai/core 0.1.13-beta.2 → 0.1.13-beta.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9517 -0
- package/README.md +19 -0
- package/dist/commands/run-tests.command.d.ts +9 -1
- package/dist/commands/run-tests.command.d.ts.map +1 -1
- package/dist/commands/run-tests.command.js +69 -6
- package/dist/commands/run-tests.command.js.map +1 -1
- package/dist/commands/seed.command.d.ts +2 -0
- package/dist/commands/seed.command.d.ts.map +1 -1
- package/dist/commands/seed.command.js +21 -0
- package/dist/commands/seed.command.js.map +1 -1
- package/dist/config/cache.options.d.ts.map +1 -1
- package/dist/config/cache.options.js +2 -2
- package/dist/config/cache.options.js.map +1 -1
- package/dist/constants/chatter-message.constants.d.ts +1 -0
- package/dist/constants/chatter-message.constants.d.ts.map +1 -1
- package/dist/constants/chatter-message.constants.js +3 -1
- package/dist/constants/chatter-message.constants.js.map +1 -1
- package/dist/constants/draft-publish-fields.d.ts +2 -0
- package/dist/constants/draft-publish-fields.d.ts.map +1 -0
- package/dist/constants/draft-publish-fields.js +5 -0
- package/dist/constants/draft-publish-fields.js.map +1 -0
- package/dist/constants/error-messages.d.ts +7 -0
- package/dist/constants/error-messages.d.ts.map +1 -1
- package/dist/constants/error-messages.js +7 -0
- package/dist/constants/error-messages.js.map +1 -1
- package/dist/constants/media-file-types.d.ts +12 -0
- package/dist/constants/media-file-types.d.ts.map +1 -1
- package/dist/constants/media-file-types.js +65 -1
- package/dist/constants/media-file-types.js.map +1 -1
- package/dist/constants/success-messages.d.ts +3 -0
- package/dist/constants/success-messages.d.ts.map +1 -1
- package/dist/constants/success-messages.js +3 -0
- package/dist/constants/success-messages.js.map +1 -1
- package/dist/constants/user.constants.d.ts +2 -0
- package/dist/constants/user.constants.d.ts.map +1 -0
- package/dist/constants/user.constants.js +5 -0
- package/dist/constants/user.constants.js.map +1 -0
- package/dist/controllers/action-metadata.controller.d.ts +1 -1
- package/dist/controllers/action-metadata.controller.js +1 -1
- package/dist/controllers/agent-event.controller.d.ts +1 -1
- package/dist/controllers/agent-event.controller.js +1 -1
- package/dist/controllers/agent-session.controller.d.ts +1 -1
- package/dist/controllers/agent-session.controller.js +1 -1
- package/dist/controllers/authentication.controller.d.ts +1 -1
- package/dist/controllers/authentication.controller.d.ts.map +1 -1
- package/dist/controllers/authentication.controller.js +4 -3
- package/dist/controllers/authentication.controller.js.map +1 -1
- package/dist/controllers/chatter-message.controller.d.ts +5 -0
- package/dist/controllers/chatter-message.controller.d.ts.map +1 -1
- package/dist/controllers/chatter-message.controller.js +14 -0
- package/dist/controllers/chatter-message.controller.js.map +1 -1
- package/dist/controllers/dashboard-user-layout.controller.d.ts +1 -1
- package/dist/controllers/dashboard-user-layout.controller.js +1 -1
- package/dist/controllers/email-template.controller.d.ts +1 -1
- package/dist/controllers/email-template.controller.js +1 -1
- package/dist/controllers/export-template.controller.d.ts +1 -1
- package/dist/controllers/export-template.controller.d.ts.map +1 -1
- package/dist/controllers/export-template.controller.js +6 -4
- package/dist/controllers/export-template.controller.js.map +1 -1
- package/dist/controllers/export-transaction.controller.d.ts +1 -1
- package/dist/controllers/export-transaction.controller.js +1 -1
- package/dist/controllers/field-metadata.controller.d.ts +2 -2
- package/dist/controllers/import-transaction-error-log.controller.d.ts +1 -1
- package/dist/controllers/import-transaction-error-log.controller.js +1 -1
- package/dist/controllers/import-transaction.controller.d.ts +1 -1
- package/dist/controllers/import-transaction.controller.d.ts.map +1 -1
- package/dist/controllers/import-transaction.controller.js +7 -7
- package/dist/controllers/import-transaction.controller.js.map +1 -1
- package/dist/controllers/list-of-values.controller.d.ts +1 -1
- package/dist/controllers/list-of-values.controller.js +1 -1
- package/dist/controllers/locale.controller.d.ts +1 -1
- package/dist/controllers/locale.controller.js +1 -1
- package/dist/controllers/mcp-audit-log.controller.d.ts +1 -1
- package/dist/controllers/mcp-audit-log.controller.js +1 -1
- package/dist/controllers/media.controller.d.ts +2 -2
- package/dist/controllers/media.controller.d.ts.map +1 -1
- package/dist/controllers/media.controller.js +13 -8
- package/dist/controllers/media.controller.js.map +1 -1
- package/dist/controllers/menu-item-metadata.controller.d.ts +1 -1
- package/dist/controllers/menu-item-metadata.controller.js +1 -1
- package/dist/controllers/model-sequence.controller.d.ts +1 -1
- package/dist/controllers/model-sequence.controller.js +1 -1
- package/dist/controllers/module-package.controller.d.ts.map +1 -1
- package/dist/controllers/module-package.controller.js +2 -3
- package/dist/controllers/module-package.controller.js.map +1 -1
- package/dist/controllers/mpin-authentication.controller.d.ts +44 -0
- package/dist/controllers/mpin-authentication.controller.d.ts.map +1 -0
- package/dist/controllers/mpin-authentication.controller.js +102 -0
- package/dist/controllers/mpin-authentication.controller.js.map +1 -0
- package/dist/controllers/mq-message-queue.controller.d.ts +1 -1
- package/dist/controllers/mq-message-queue.controller.js +1 -1
- package/dist/controllers/mq-message.controller.d.ts +1 -1
- package/dist/controllers/mq-message.controller.js +1 -1
- package/dist/controllers/permission-metadata.controller.d.ts +1 -1
- package/dist/controllers/permission-metadata.controller.js +1 -1
- package/dist/controllers/role-metadata.controller.d.ts +1 -1
- package/dist/controllers/role-metadata.controller.js +1 -1
- package/dist/controllers/saved-filters.controller.d.ts +1 -1
- package/dist/controllers/saved-filters.controller.js +1 -1
- package/dist/controllers/scheduled-job.controller.d.ts +1 -1
- package/dist/controllers/scheduled-job.controller.js +1 -1
- package/dist/controllers/security-rule.controller.d.ts +1 -1
- package/dist/controllers/security-rule.controller.js +1 -1
- package/dist/controllers/sms-template.controller.d.ts +1 -1
- package/dist/controllers/sms-template.controller.js +1 -1
- package/dist/controllers/user-activity-history.controller.d.ts +1 -1
- package/dist/controllers/user-activity-history.controller.js +1 -1
- package/dist/controllers/user-view-metadata.controller.d.ts +1 -1
- package/dist/controllers/user-view-metadata.controller.js +1 -1
- package/dist/controllers/user.controller.d.ts +2 -2
- package/dist/controllers/user.controller.js +2 -2
- package/dist/controllers/view-metadata.controller.d.ts +2 -2
- package/dist/controllers/view-metadata.controller.js +1 -1
- package/dist/controllers/workflow-definition.controller.d.ts +1 -1
- package/dist/controllers/workflow-definition.controller.js +1 -1
- package/dist/controllers/workflow-execution-artifact.controller.d.ts +1 -1
- package/dist/controllers/workflow-execution-artifact.controller.js +1 -1
- package/dist/controllers/workflow-execution-log.controller.d.ts +1 -1
- package/dist/controllers/workflow-execution-log.controller.js +1 -1
- package/dist/controllers/workflow-execution.controller.d.ts +1 -1
- package/dist/controllers/workflow-execution.controller.js +1 -1
- package/dist/controllers/workflow-secret.controller.d.ts +1 -1
- package/dist/controllers/workflow-secret.controller.js +1 -1
- package/dist/controllers/workflow-step-execution.controller.d.ts +1 -1
- package/dist/controllers/workflow-step-execution.controller.js +1 -1
- package/dist/controllers/workflow-trigger-execution.controller.d.ts +1 -1
- package/dist/controllers/workflow-trigger-execution.controller.js +1 -1
- package/dist/dtos/basic-filters.dto.d.ts +0 -1
- package/dist/dtos/basic-filters.dto.d.ts.map +1 -1
- package/dist/dtos/basic-filters.dto.js +1 -6
- package/dist/dtos/basic-filters.dto.js.map +1 -1
- package/dist/dtos/change-mpin.dto.d.ts +6 -0
- package/dist/dtos/change-mpin.dto.d.ts.map +1 -0
- package/dist/dtos/change-mpin.dto.js +39 -0
- package/dist/dtos/change-mpin.dto.js.map +1 -0
- package/dist/dtos/create-field-metadata.dto.d.ts +2 -6
- package/dist/dtos/create-field-metadata.dto.d.ts.map +1 -1
- package/dist/dtos/create-field-metadata.dto.js +4 -10
- package/dist/dtos/create-field-metadata.dto.js.map +1 -1
- package/dist/dtos/create-model-sequence.dto.d.ts +1 -1
- package/dist/dtos/create-model-sequence.dto.d.ts.map +1 -1
- package/dist/dtos/create-model-sequence.dto.js +1 -1
- package/dist/dtos/create-model-sequence.dto.js.map +1 -1
- package/dist/dtos/mpin-login.dto.d.ts +5 -0
- package/dist/dtos/mpin-login.dto.d.ts.map +1 -0
- package/dist/dtos/mpin-login.dto.js +33 -0
- package/dist/dtos/mpin-login.dto.js.map +1 -0
- package/dist/dtos/setup-mpin.dto.d.ts +7 -0
- package/dist/dtos/setup-mpin.dto.d.ts.map +1 -0
- package/dist/dtos/setup-mpin.dto.js +47 -0
- package/dist/dtos/setup-mpin.dto.js.map +1 -0
- package/dist/entities/common.entity.d.ts +4 -0
- package/dist/entities/common.entity.d.ts.map +1 -1
- package/dist/entities/common.entity.js +25 -1
- package/dist/entities/common.entity.js.map +1 -1
- package/dist/entities/dashboard-user-layout.entity.js +2 -2
- package/dist/entities/dashboard-user-layout.entity.js.map +1 -1
- package/dist/entities/saved-filters.entity.js +3 -3
- package/dist/entities/saved-filters.entity.js.map +1 -1
- package/dist/entities/user-device-credential.entity.d.ts +18 -0
- package/dist/entities/user-device-credential.entity.d.ts.map +1 -0
- package/dist/entities/user-device-credential.entity.js +91 -0
- package/dist/entities/user-device-credential.entity.js.map +1 -0
- package/dist/entities/user.entity.d.ts.map +1 -1
- package/dist/entities/user.entity.js +36 -0
- package/dist/entities/user.entity.js.map +1 -1
- package/dist/entities/view-metadata.entity.js.map +1 -1
- package/dist/entities/workflow-execution-artifact.entity.d.ts.map +1 -1
- package/dist/entities/workflow-execution-artifact.entity.js +4 -3
- package/dist/entities/workflow-execution-artifact.entity.js.map +1 -1
- package/dist/entities/workflow-execution-log.entity.d.ts.map +1 -1
- package/dist/entities/workflow-execution-log.entity.js +4 -3
- package/dist/entities/workflow-execution-log.entity.js.map +1 -1
- package/dist/entities/workflow-execution.entity.d.ts.map +1 -1
- package/dist/entities/workflow-execution.entity.js +3 -2
- package/dist/entities/workflow-execution.entity.js.map +1 -1
- package/dist/entities/workflow-secret.entity.d.ts.map +1 -1
- package/dist/entities/workflow-secret.entity.js +3 -2
- package/dist/entities/workflow-secret.entity.js.map +1 -1
- package/dist/entities/workflow-step-execution.entity.d.ts.map +1 -1
- package/dist/entities/workflow-step-execution.entity.js +3 -2
- package/dist/entities/workflow-step-execution.entity.js.map +1 -1
- package/dist/entities/workflow-trigger-execution.entity.d.ts.map +1 -1
- package/dist/entities/workflow-trigger-execution.entity.js +7 -6
- package/dist/entities/workflow-trigger-execution.entity.js.map +1 -1
- package/dist/factories/mail.factory.d.ts.map +1 -1
- package/dist/factories/mail.factory.js +4 -0
- package/dist/factories/mail.factory.js.map +1 -1
- package/dist/filters/http-exception.filter.d.ts.map +1 -1
- package/dist/filters/http-exception.filter.js +5 -1
- package/dist/filters/http-exception.filter.js.map +1 -1
- package/dist/helpers/bootstrap.helper.d.ts +5 -2
- package/dist/helpers/bootstrap.helper.d.ts.map +1 -1
- package/dist/helpers/bootstrap.helper.js +7 -3
- package/dist/helpers/bootstrap.helper.js.map +1 -1
- package/dist/helpers/field-crud-managers/MediaFieldCrudManager.d.ts +1 -0
- package/dist/helpers/field-crud-managers/MediaFieldCrudManager.d.ts.map +1 -1
- package/dist/helpers/field-crud-managers/MediaFieldCrudManager.js +18 -11
- package/dist/helpers/field-crud-managers/MediaFieldCrudManager.js.map +1 -1
- package/dist/helpers/file-download.helper.d.ts +9 -0
- package/dist/helpers/file-download.helper.d.ts.map +1 -0
- package/dist/helpers/file-download.helper.js +19 -0
- package/dist/helpers/file-download.helper.js.map +1 -0
- package/dist/helpers/model-metadata-helper.service.d.ts.map +1 -1
- package/dist/helpers/model-metadata-helper.service.js +30 -1
- package/dist/helpers/model-metadata-helper.service.js.map +1 -1
- package/dist/helpers/security.helper.d.ts +5 -1
- package/dist/helpers/security.helper.d.ts.map +1 -1
- package/dist/helpers/security.helper.js +15 -4
- package/dist/helpers/security.helper.js.map +1 -1
- package/dist/helpers/solid-core-error-codes-provider.service.d.ts.map +1 -1
- package/dist/helpers/solid-core-error-codes-provider.service.js +45 -0
- package/dist/helpers/solid-core-error-codes-provider.service.js.map +1 -1
- package/dist/helpers/solid-microservice-adapter.service.d.ts.map +1 -1
- package/dist/helpers/solid-microservice-adapter.service.js +1 -1
- package/dist/helpers/solid-microservice-adapter.service.js.map +1 -1
- package/dist/helpers/solid-registry.js +1 -1
- package/dist/helpers/solid-registry.js.map +1 -1
- package/dist/helpers/typeorm-db-helper.d.ts.map +1 -1
- package/dist/helpers/typeorm-db-helper.js +9 -0
- package/dist/helpers/typeorm-db-helper.js.map +1 -1
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/dist/interfaces/active-user-data.interface.d.ts +1 -0
- package/dist/interfaces/active-user-data.interface.d.ts.map +1 -1
- package/dist/interfaces/active-user-data.interface.js.map +1 -1
- package/dist/interfaces.d.ts +1 -0
- package/dist/interfaces.d.ts.map +1 -1
- package/dist/interfaces.js.map +1 -1
- package/dist/repository/user-device-credential.repository.d.ts +12 -0
- package/dist/repository/user-device-credential.repository.d.ts.map +1 -0
- package/dist/repository/user-device-credential.repository.js +34 -0
- package/dist/repository/user-device-credential.repository.js.map +1 -0
- package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
- package/dist/seeders/module-metadata-seeder.service.js +51 -30
- package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
- package/dist/seeders/module-test-data.service.d.ts +3 -1
- package/dist/seeders/module-test-data.service.d.ts.map +1 -1
- package/dist/seeders/module-test-data.service.js +8 -2
- package/dist/seeders/module-test-data.service.js.map +1 -1
- package/dist/seeders/seed-data/solid-core-metadata.json +290 -78
- package/dist/services/api-key.service.d.ts.map +1 -1
- package/dist/services/api-key.service.js +1 -1
- package/dist/services/api-key.service.js.map +1 -1
- package/dist/services/authentication.service.d.ts +9 -5
- package/dist/services/authentication.service.d.ts.map +1 -1
- package/dist/services/authentication.service.js +102 -23
- package/dist/services/authentication.service.js.map +1 -1
- package/dist/services/chatter-message.service.d.ts +15 -1
- package/dist/services/chatter-message.service.d.ts.map +1 -1
- package/dist/services/chatter-message.service.js +56 -4
- package/dist/services/chatter-message.service.js.map +1 -1
- package/dist/services/crud-helper.service.d.ts +6 -4
- package/dist/services/crud-helper.service.d.ts.map +1 -1
- package/dist/services/crud-helper.service.js +32 -48
- package/dist/services/crud-helper.service.js.map +1 -1
- package/dist/services/crud.service.d.ts +13 -1
- package/dist/services/crud.service.d.ts.map +1 -1
- package/dist/services/crud.service.js +93 -68
- package/dist/services/crud.service.js.map +1 -1
- package/dist/services/datasource-introspection.service.d.ts.map +1 -1
- package/dist/services/datasource-introspection.service.js.map +1 -1
- package/dist/services/draft-publish-helper.service.d.ts +53 -0
- package/dist/services/draft-publish-helper.service.d.ts.map +1 -0
- package/dist/services/draft-publish-helper.service.js +402 -0
- package/dist/services/draft-publish-helper.service.js.map +1 -0
- package/dist/services/field-metadata.service.d.ts +2 -2
- package/dist/services/field-metadata.service.d.ts.map +1 -1
- package/dist/services/field-metadata.service.js +2 -1
- package/dist/services/field-metadata.service.js.map +1 -1
- package/dist/services/internationalisation-helper.service.d.ts +19 -0
- package/dist/services/internationalisation-helper.service.d.ts.map +1 -0
- package/dist/services/internationalisation-helper.service.js +107 -0
- package/dist/services/internationalisation-helper.service.js.map +1 -0
- package/dist/services/locale.service.d.ts +1 -1
- package/dist/services/media-storage.utils.d.ts +1 -0
- package/dist/services/media-storage.utils.d.ts.map +1 -1
- package/dist/services/media-storage.utils.js +12 -1
- package/dist/services/media-storage.utils.js.map +1 -1
- package/dist/services/media.service.d.ts +3 -2
- package/dist/services/media.service.d.ts.map +1 -1
- package/dist/services/media.service.js +44 -29
- package/dist/services/media.service.js.map +1 -1
- package/dist/services/metadata-validation.service.d.ts +7 -0
- package/dist/services/metadata-validation.service.d.ts.map +1 -0
- package/dist/services/metadata-validation.service.js +42 -0
- package/dist/services/metadata-validation.service.js.map +1 -0
- package/dist/services/model-metadata.service.d.ts.map +1 -1
- package/dist/services/model-metadata.service.js +2 -1
- package/dist/services/model-metadata.service.js.map +1 -1
- package/dist/services/module-metadata.service.d.ts.map +1 -1
- package/dist/services/module-metadata.service.js +2 -1
- package/dist/services/module-metadata.service.js.map +1 -1
- package/dist/services/mpin.service.d.ts +67 -0
- package/dist/services/mpin.service.d.ts.map +1 -0
- package/dist/services/mpin.service.js +282 -0
- package/dist/services/mpin.service.js.map +1 -0
- package/dist/services/object.utils.d.ts +2 -0
- package/dist/services/object.utils.d.ts.map +1 -0
- package/dist/services/object.utils.js +11 -0
- package/dist/services/object.utils.js.map +1 -0
- package/dist/services/refresh-token-ids-storage.service.d.ts +25 -6
- package/dist/services/refresh-token-ids-storage.service.d.ts.map +1 -1
- package/dist/services/refresh-token-ids-storage.service.js +80 -42
- package/dist/services/refresh-token-ids-storage.service.js.map +1 -1
- package/dist/services/setting.service.d.ts.map +1 -1
- package/dist/services/setting.service.js +2 -1
- package/dist/services/setting.service.js.map +1 -1
- package/dist/services/settings/default-settings-provider.service.d.ts +228 -12
- package/dist/services/settings/default-settings-provider.service.d.ts.map +1 -1
- package/dist/services/settings/default-settings-provider.service.js +129 -6
- package/dist/services/settings/default-settings-provider.service.js.map +1 -1
- package/dist/services/user.service.d.ts +2 -2
- package/dist/services/user.service.d.ts.map +1 -1
- package/dist/services/user.service.js +9 -3
- package/dist/services/user.service.js.map +1 -1
- package/dist/services/view-metadata.service.d.ts +1 -2
- package/dist/services/view-metadata.service.d.ts.map +1 -1
- package/dist/services/view-metadata.service.js +3 -55
- package/dist/services/view-metadata.service.js.map +1 -1
- package/dist/services/workflow/nodes/execution-fail.node.d.ts +6 -0
- package/dist/services/workflow/nodes/execution-fail.node.d.ts.map +1 -0
- package/dist/services/workflow/nodes/execution-fail.node.js +81 -0
- package/dist/services/workflow/nodes/execution-fail.node.js.map +1 -0
- package/dist/services/workflow/nodes/for-each.node.d.ts +2 -0
- package/dist/services/workflow/nodes/for-each.node.d.ts.map +1 -1
- package/dist/services/workflow/nodes/for-each.node.js +114 -5
- package/dist/services/workflow/nodes/for-each.node.js.map +1 -1
- package/dist/services/workflow/nodes/if.node.d.ts.map +1 -1
- package/dist/services/workflow/nodes/if.node.js +7 -0
- package/dist/services/workflow/nodes/if.node.js.map +1 -1
- package/dist/services/workflow/nodes/log-write.node.d.ts.map +1 -1
- package/dist/services/workflow/nodes/log-write.node.js +2 -0
- package/dist/services/workflow/nodes/log-write.node.js.map +1 -1
- package/dist/services/workflow/nodes/loop-until.node.d.ts +12 -0
- package/dist/services/workflow/nodes/loop-until.node.d.ts.map +1 -0
- package/dist/services/workflow/nodes/loop-until.node.js +281 -0
- package/dist/services/workflow/nodes/loop-until.node.js.map +1 -0
- package/dist/services/workflow/nodes/parallel.node.d.ts.map +1 -1
- package/dist/services/workflow/nodes/parallel.node.js +7 -0
- package/dist/services/workflow/nodes/parallel.node.js.map +1 -1
- package/dist/services/workflow/nodes/sequential.node.d.ts.map +1 -1
- package/dist/services/workflow/nodes/sequential.node.js +7 -0
- package/dist/services/workflow/nodes/sequential.node.js.map +1 -1
- package/dist/services/workflow/nodes/switch.node.d.ts.map +1 -1
- package/dist/services/workflow/nodes/switch.node.js +7 -0
- package/dist/services/workflow/nodes/switch.node.js.map +1 -1
- package/dist/services/workflow/workflow-definition-validator.service.d.ts.map +1 -1
- package/dist/services/workflow/workflow-definition-validator.service.js +5 -2
- package/dist/services/workflow/workflow-definition-validator.service.js.map +1 -1
- package/dist/services/workflow/workflow-expression.service.d.ts.map +1 -1
- package/dist/services/workflow/workflow-expression.service.js +5 -0
- package/dist/services/workflow/workflow-expression.service.js.map +1 -1
- package/dist/services/workflow/workflow-node-registry.service.d.ts +2 -1
- package/dist/services/workflow/workflow-node-registry.service.d.ts.map +1 -1
- package/dist/services/workflow/workflow-node-registry.service.js +7 -0
- package/dist/services/workflow/workflow-node-registry.service.js.map +1 -1
- package/dist/services/workflow/workflow-runtime.service.d.ts +8 -0
- package/dist/services/workflow/workflow-runtime.service.d.ts.map +1 -1
- package/dist/services/workflow/workflow-runtime.service.js +197 -32
- package/dist/services/workflow/workflow-runtime.service.js.map +1 -1
- package/dist/services/workflow-definition.service.d.ts +1 -1
- package/dist/solid-core.module.d.ts +1 -3
- package/dist/solid-core.module.d.ts.map +1 -1
- package/dist/solid-core.module.js +22 -40
- package/dist/solid-core.module.js.map +1 -1
- package/dist/testing/adapters/ui/playwright-adapter.d.ts +5 -0
- package/dist/testing/adapters/ui/playwright-adapter.d.ts.map +1 -1
- package/dist/testing/adapters/ui/playwright-adapter.js +17 -1
- package/dist/testing/adapters/ui/playwright-adapter.js.map +1 -1
- package/dist/testing/adapters/ui/ui.types.d.ts +2 -0
- package/dist/testing/adapters/ui/ui.types.d.ts.map +1 -1
- package/dist/testing/adapters/ui/ui.types.js.map +1 -1
- package/dist/testing/reporter/console-reporter.d.ts +3 -0
- package/dist/testing/reporter/console-reporter.d.ts.map +1 -1
- package/dist/testing/reporter/console-reporter.js +2 -0
- package/dist/testing/reporter/console-reporter.js.map +1 -1
- package/dist/testing/reporter/progress-reporter.d.ts +64 -0
- package/dist/testing/reporter/progress-reporter.d.ts.map +1 -0
- package/dist/testing/reporter/progress-reporter.js +224 -0
- package/dist/testing/reporter/progress-reporter.js.map +1 -0
- package/dist/testing/reporter/reporter.types.d.ts +3 -0
- package/dist/testing/reporter/reporter.types.d.ts.map +1 -1
- package/dist/testing/reporter/reporter.types.js.map +1 -1
- package/dist/testing/runner/run-from-metadata.d.ts.map +1 -1
- package/dist/testing/runner/run-from-metadata.js +1 -0
- package/dist/testing/runner/run-from-metadata.js.map +1 -1
- package/dist/testing/steps/ui/actions.step.d.ts.map +1 -1
- package/dist/testing/steps/ui/actions.step.js +6 -2
- package/dist/testing/steps/ui/actions.step.js.map +1 -1
- package/dist/testing/steps/ui/assertions.step.d.ts.map +1 -1
- package/dist/testing/steps/ui/assertions.step.js +11 -3
- package/dist/testing/steps/ui/assertions.step.js.map +1 -1
- package/dist/testing/steps/ui/form.step.d.ts.map +1 -1
- package/dist/testing/steps/ui/form.step.js +9 -3
- package/dist/testing/steps/ui/form.step.js.map +1 -1
- package/dist/testing/steps/ui/manual.step.js +1 -1
- package/dist/testing/steps/ui/manual.step.js.map +1 -1
- package/dist/testing/steps/ui/navigation.step.d.ts.map +1 -1
- package/dist/testing/steps/ui/navigation.step.js +3 -1
- package/dist/testing/steps/ui/navigation.step.js.map +1 -1
- package/dist/theme/theme-registry.d.ts +14 -0
- package/dist/theme/theme-registry.d.ts.map +1 -0
- package/dist/theme/theme-registry.js +147 -0
- package/dist/theme/theme-registry.js.map +1 -0
- package/dist/types/workflow-dsl.types.d.ts +14 -0
- package/dist/types/workflow-dsl.types.d.ts.map +1 -1
- package/dist/types/workflow-dsl.types.js.map +1 -1
- package/package.json +2 -2
- package/postman/mpin.postman_collection.json +1045 -0
- package/src/commands/run-tests.command.ts +64 -4
- package/src/commands/seed.command.ts +19 -0
- package/src/config/cache.options.ts +9 -1
- package/src/constants/chatter-message.constants.ts +12 -0
- package/src/constants/draft-publish-fields.ts +5 -0
- package/src/constants/error-messages.ts +14 -0
- package/src/constants/media-file-types.ts +127 -0
- package/src/constants/success-messages.ts +5 -0
- package/src/constants/user.constants.ts +13 -0
- package/src/controllers/authentication.controller.ts +5 -2
- package/src/controllers/chatter-message.controller.ts +8 -0
- package/src/controllers/export-template.controller.ts +5 -3
- package/src/controllers/import-transaction.controller.ts +6 -6
- package/src/controllers/media.controller.ts +17 -6
- package/src/controllers/module-package.controller.ts +2 -3
- package/src/controllers/mpin-authentication.controller.ts +67 -0
- package/src/dtos/basic-filters.dto.ts +0 -4
- package/src/dtos/change-mpin.dto.ts +18 -0
- package/src/dtos/create-field-metadata.dto.ts +4 -9
- package/src/dtos/create-model-sequence.dto.ts +1 -1
- package/src/dtos/mpin-login.dto.ts +24 -0
- package/src/dtos/setup-mpin.dto.ts +51 -0
- package/src/entities/common.entity.ts +24 -0
- package/src/entities/dashboard-user-layout.entity.ts +2 -2
- package/src/entities/saved-filters.entity.ts +3 -3
- package/src/entities/user-device-credential.entity.ts +130 -0
- package/src/entities/user.entity.ts +53 -36
- package/src/entities/view-metadata.entity.ts +2 -2
- package/src/entities/workflow-execution-artifact.entity.ts +4 -3
- package/src/entities/workflow-execution-log.entity.ts +4 -3
- package/src/entities/workflow-execution.entity.ts +3 -2
- package/src/entities/workflow-secret.entity.ts +3 -2
- package/src/entities/workflow-step-execution.entity.ts +3 -2
- package/src/entities/workflow-trigger-execution.entity.ts +7 -6
- package/src/factories/mail.factory.ts +7 -1
- package/src/filters/http-exception.filter.ts +11 -1
- package/src/helpers/bootstrap.helper.ts +43 -6
- package/src/helpers/field-crud-managers/MediaFieldCrudManager.ts +26 -12
- package/src/helpers/file-download.helper.ts +45 -0
- package/src/helpers/model-metadata-helper.service.ts +32 -1
- package/src/helpers/security.helper.ts +35 -8
- package/src/helpers/solid-core-error-codes-provider.service.ts +53 -0
- package/src/helpers/solid-microservice-adapter.service.ts +6 -1
- package/src/helpers/solid-registry.ts +1 -1
- package/src/helpers/typeorm-db-helper.ts +12 -1
- package/src/index.ts +10 -0
- package/src/interfaces/active-user-data.interface.ts +9 -0
- package/src/interfaces.ts +1 -0
- package/src/repository/user-device-credential.repository.ts +17 -0
- package/src/seeders/module-metadata-seeder.service.ts +61 -39
- package/src/seeders/module-test-data.service.ts +6 -0
- package/src/seeders/seed-data/solid-core-metadata.json +290 -78
- package/src/services/api-key.service.ts +13 -1
- package/src/services/authentication.service.ts +203 -41
- package/src/services/chatter-message.service.ts +124 -5
- package/src/services/crud-helper.service.ts +35 -50
- package/src/services/crud.service.ts +131 -105
- package/src/services/datasource-introspection.service.ts +5 -0
- package/src/services/draft-publish-helper.service.ts +581 -0
- package/src/services/field-metadata.service.ts +3 -2
- package/src/services/internationalisation-helper.service.ts +148 -0
- package/src/services/media-storage.utils.ts +26 -1
- package/src/services/media.service.ts +67 -36
- package/src/services/metadata-validation.service.ts +39 -0
- package/src/services/model-metadata.service.ts +4 -1
- package/src/services/module-metadata.service.ts +3 -1
- package/src/services/mpin.service.ts +447 -0
- package/src/services/object.utils.ts +10 -0
- package/src/services/refresh-token-ids-storage.service.ts +229 -90
- package/src/services/setting.service.ts +3 -1
- package/src/services/settings/default-settings-provider.service.ts +140 -6
- package/src/services/user.service.ts +18 -3
- package/src/services/view-metadata.service.ts +5 -145
- package/src/services/workflow/nodes/execution-fail.node.ts +82 -0
- package/src/services/workflow/nodes/for-each.node.ts +140 -6
- package/src/services/workflow/nodes/if.node.ts +7 -0
- package/src/services/workflow/nodes/log-write.node.ts +2 -0
- package/src/services/workflow/nodes/loop-until.node.ts +343 -0
- package/src/services/workflow/nodes/parallel.node.ts +7 -0
- package/src/services/workflow/nodes/sequential.node.ts +7 -0
- package/src/services/workflow/nodes/switch.node.ts +7 -0
- package/src/services/workflow/workflow-definition-validator.service.ts +5 -2
- package/src/services/workflow/workflow-expression.service.ts +5 -0
- package/src/services/workflow/workflow-node-registry.service.ts +11 -0
- package/src/services/workflow/workflow-runtime.service.ts +274 -39
- package/src/solid-core.module.ts +33 -15
- package/src/testing/README.md +75 -3
- package/src/testing/adapters/ui/playwright-adapter.ts +25 -0
- package/src/testing/adapters/ui/ui.types.ts +2 -0
- package/src/testing/reporter/console-reporter.ts +4 -0
- package/src/testing/reporter/progress-reporter.ts +251 -0
- package/src/testing/reporter/reporter.types.ts +3 -0
- package/src/testing/runner/run-from-metadata.ts +1 -0
- package/src/testing/steps/ui/actions.step.ts +8 -4
- package/src/testing/steps/ui/assertions.step.ts +18 -5
- package/src/testing/steps/ui/form.step.ts +16 -6
- package/src/testing/steps/ui/manual.step.ts +1 -1
- package/src/testing/steps/ui/navigation.step.ts +4 -2
- package/src/theme/theme-registry.ts +181 -0
- package/src/types/workflow-dsl.types.ts +43 -0
|
@@ -20,7 +20,7 @@ import { SUCCESS_MESSAGES } from "src/constants/success-messages";
|
|
|
20
20
|
import { CreateUserDto } from "src/dtos/create-user.dto";
|
|
21
21
|
import { MailFactory } from "src/factories/mail.factory";
|
|
22
22
|
import { UserRepository } from "src/repository/user.repository";
|
|
23
|
-
import { DataSource, DeepPartial, Repository } from "typeorm";
|
|
23
|
+
import { DataSource, DeepPartial, FindOptionsWhere, Repository } from "typeorm";
|
|
24
24
|
import { v4 as uuidv4 } from "uuid";
|
|
25
25
|
import {
|
|
26
26
|
ForgotPasswordSendVerificationTokenOn,
|
|
@@ -49,6 +49,7 @@ import { SsoCodeStorageService } from "./sso-code-storage.service";
|
|
|
49
49
|
import { RoleMetadataService } from "./role-metadata.service";
|
|
50
50
|
import { SettingService } from "./setting.service";
|
|
51
51
|
import { UserActivityHistoryService } from "./user-activity-history.service";
|
|
52
|
+
import { MetadataValidationService } from "./metadata-validation.service";
|
|
52
53
|
import { UserService } from "./user.service";
|
|
53
54
|
import { SmsFactory } from "src/factories/sms.factory";
|
|
54
55
|
import { WhatsAppFactory } from "src/factories/whatsapp.factory";
|
|
@@ -88,6 +89,7 @@ export class AuthenticationService {
|
|
|
88
89
|
private readonly roleMetadataService: RoleMetadataService,
|
|
89
90
|
private readonly userActivityHistoryService: UserActivityHistoryService,
|
|
90
91
|
private readonly ssoCodeStorage: SsoCodeStorageService,
|
|
92
|
+
private readonly metadataValidationService: MetadataValidationService,
|
|
91
93
|
|
|
92
94
|
@InjectDataSource()
|
|
93
95
|
private readonly dataSource: DataSource,
|
|
@@ -225,6 +227,9 @@ export class AuthenticationService {
|
|
|
225
227
|
preferEntityApiKeyFlag: boolean = false,
|
|
226
228
|
): Promise<T> {
|
|
227
229
|
try {
|
|
230
|
+
await this.assertUniqueSignupIdentifiers(signUpDto, repo);
|
|
231
|
+
await this.metadataValidationService.validateCreateDto("user", signUpDto);
|
|
232
|
+
|
|
228
233
|
const onForcePasswordChange =
|
|
229
234
|
this.settingService.getConfigValue<SolidCoreSetting>(
|
|
230
235
|
"forceChangePasswordOnFirstLogin",
|
|
@@ -276,6 +281,59 @@ export class AuthenticationService {
|
|
|
276
281
|
}
|
|
277
282
|
}
|
|
278
283
|
|
|
284
|
+
private async assertUniqueSignupIdentifiers<T extends User>(
|
|
285
|
+
signUpDto: SignUpDto,
|
|
286
|
+
repo: Repository<T>,
|
|
287
|
+
): Promise<void> {
|
|
288
|
+
const username = signUpDto.username?.trim();
|
|
289
|
+
const email = signUpDto.email?.trim();
|
|
290
|
+
const mobile = signUpDto.mobile?.trim();
|
|
291
|
+
|
|
292
|
+
const where: FindOptionsWhere<T>[] = [];
|
|
293
|
+
|
|
294
|
+
if (username) {
|
|
295
|
+
where.push({ username } as FindOptionsWhere<T>);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (email) {
|
|
299
|
+
where.push({ email } as FindOptionsWhere<T>);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (mobile) {
|
|
303
|
+
where.push({ mobile } as FindOptionsWhere<T>);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (where.length === 0) {
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const existingUser = await repo.findOne({ where });
|
|
311
|
+
|
|
312
|
+
if (!existingUser) {
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (email && existingUser.email === email) {
|
|
317
|
+
throw new ConflictException(
|
|
318
|
+
`A user with email address "${email}" already exists.`,
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (mobile && existingUser.mobile === mobile) {
|
|
323
|
+
throw new ConflictException(
|
|
324
|
+
`A user with mobile number "${mobile}" already exists.`,
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (username && existingUser.username === username) {
|
|
329
|
+
throw new ConflictException(
|
|
330
|
+
`A user with username "${username}" already exists.`,
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
throw new ConflictException(ERROR_MESSAGES.USER_ALREADY_EXISTS);
|
|
335
|
+
}
|
|
336
|
+
|
|
279
337
|
private async handlePasswordlessSignupOtp<T extends User>(
|
|
280
338
|
user: T,
|
|
281
339
|
signUpDto: SignUpDto,
|
|
@@ -400,11 +458,23 @@ export class AuthenticationService {
|
|
|
400
458
|
// if forcePasswordChange is true, then we trigger an email to the user to change the password, this needs to be done using a queue.
|
|
401
459
|
// Create a new method like notifyUserOnForcePasswordChange, create a new email template we can call it on-force-password-change this template to include the random password
|
|
402
460
|
if (user.forcePasswordChange && autoGeneratedPwd) {
|
|
403
|
-
|
|
461
|
+
try {
|
|
462
|
+
await this.notifyUserOnForcePasswordChange(user, autoGeneratedPwd);
|
|
463
|
+
} catch (error: any) {
|
|
464
|
+
this.logger.error(
|
|
465
|
+
`Failed to send force-password-change email for user ${user.id}: ${error?.message ?? error}`,
|
|
466
|
+
);
|
|
467
|
+
}
|
|
404
468
|
}
|
|
405
469
|
|
|
406
470
|
// Send welcome notifications (email/SMS) if enabled.
|
|
407
|
-
|
|
471
|
+
try {
|
|
472
|
+
await this.notifyUserOnSignup(user);
|
|
473
|
+
} catch (error: any) {
|
|
474
|
+
this.logger.error(
|
|
475
|
+
`Failed to send signup notifications for user ${user.id}: ${error?.message ?? error}`,
|
|
476
|
+
);
|
|
477
|
+
}
|
|
408
478
|
}
|
|
409
479
|
|
|
410
480
|
generatePassword(length: number = 8): string {
|
|
@@ -1578,7 +1648,7 @@ export class AuthenticationService {
|
|
|
1578
1648
|
// Assuming all users do not have mobile as mandatory.
|
|
1579
1649
|
if (
|
|
1580
1650
|
forgotPasswordSendVerificationTokenOn ==
|
|
1581
|
-
|
|
1651
|
+
ForgotPasswordSendVerificationTokenOn.MOBILE &&
|
|
1582
1652
|
user.mobile
|
|
1583
1653
|
) {
|
|
1584
1654
|
const smsService = this.smsFactory.getSmsService();
|
|
@@ -1702,7 +1772,7 @@ export class AuthenticationService {
|
|
|
1702
1772
|
// Assuming all users do not have mobile as mandatory.
|
|
1703
1773
|
if (
|
|
1704
1774
|
forgotPasswordSendVerificationTokenOn ==
|
|
1705
|
-
|
|
1775
|
+
ForgotPasswordSendVerificationTokenOn.MOBILE &&
|
|
1706
1776
|
user.mobile
|
|
1707
1777
|
) {
|
|
1708
1778
|
const smsService = this.smsFactory.getSmsService();
|
|
@@ -1723,7 +1793,13 @@ export class AuthenticationService {
|
|
|
1723
1793
|
}
|
|
1724
1794
|
}
|
|
1725
1795
|
|
|
1726
|
-
|
|
1796
|
+
/**
|
|
1797
|
+
* @param deviceId optional stable per-device identifier. Supplied by callers
|
|
1798
|
+
* that own a device credential; otherwise a key is minted per login so that
|
|
1799
|
+
* clients with no device concept (the web UI) still get their own bucket
|
|
1800
|
+
* instead of sharing - and evicting - one.
|
|
1801
|
+
*/
|
|
1802
|
+
async generateTokens(user: User, deviceId?: string) {
|
|
1727
1803
|
const sessionId = this.shouldPreventConcurrentLogins()
|
|
1728
1804
|
? randomUUID()
|
|
1729
1805
|
: undefined;
|
|
@@ -1732,9 +1808,17 @@ export class AuthenticationService {
|
|
|
1732
1808
|
} else {
|
|
1733
1809
|
await this.activeSessionStorage.clearActiveSession(user.id);
|
|
1734
1810
|
}
|
|
1811
|
+
|
|
1812
|
+
// Mutually exclusive with sessionId by construction: per-device buckets
|
|
1813
|
+
// exist only when preventConcurrentLogins is off, sessionId only when it
|
|
1814
|
+
// is on.
|
|
1815
|
+
const deviceKey = this.refreshTokenIdsStorage.areConcurrentLoginsAllowed()
|
|
1816
|
+
? deviceId ?? randomUUID()
|
|
1817
|
+
: undefined;
|
|
1818
|
+
|
|
1735
1819
|
const [accessToken, refreshToken] = await Promise.all([
|
|
1736
|
-
await this.generateAccessToken(user, sessionId),
|
|
1737
|
-
await this.generateRefreshToken(user),
|
|
1820
|
+
await this.generateAccessToken(user, sessionId, deviceKey),
|
|
1821
|
+
await this.generateRefreshToken(user, undefined, deviceKey),
|
|
1738
1822
|
]);
|
|
1739
1823
|
|
|
1740
1824
|
return {
|
|
@@ -1743,7 +1827,7 @@ export class AuthenticationService {
|
|
|
1743
1827
|
};
|
|
1744
1828
|
}
|
|
1745
1829
|
|
|
1746
|
-
async generateAccessToken(user: User, sessionId?: string) {
|
|
1830
|
+
async generateAccessToken(user: User, sessionId?: string, deviceKey?: string) {
|
|
1747
1831
|
// const userRoleNames = user.roles.map((role) => role.name).join(';')
|
|
1748
1832
|
const userRoleNames = user.roles.map((role) => role.name);
|
|
1749
1833
|
const resolvedSessionId = this.shouldPreventConcurrentLogins()
|
|
@@ -1760,18 +1844,36 @@ export class AuthenticationService {
|
|
|
1760
1844
|
email: user.email,
|
|
1761
1845
|
roles: userRoleNames,
|
|
1762
1846
|
...(resolvedSessionId ? { sessionId: resolvedSessionId } : {}),
|
|
1847
|
+
// Carried so that bearer-authenticated endpoints which need this
|
|
1848
|
+
// session's refresh token - generateSsoCode, me - can locate its
|
|
1849
|
+
// bucket. The refresh token is not available to them.
|
|
1850
|
+
...(deviceKey ? { deviceKey } : {}),
|
|
1763
1851
|
},
|
|
1764
1852
|
);
|
|
1765
1853
|
|
|
1766
1854
|
return accessToken;
|
|
1767
1855
|
}
|
|
1768
1856
|
|
|
1769
|
-
async generateRefreshToken(
|
|
1857
|
+
async generateRefreshToken(
|
|
1858
|
+
user: User,
|
|
1859
|
+
previousRefreshToken?: string,
|
|
1860
|
+
deviceKey?: string,
|
|
1861
|
+
) {
|
|
1770
1862
|
const refreshTokenId = randomUUID();
|
|
1771
1863
|
const refreshTokenTtl =
|
|
1772
1864
|
this.settingService.getConfigValue<SolidCoreSetting>("refreshTokenTtl");
|
|
1865
|
+
|
|
1866
|
+
// Only per-device tokens carry an epoch - it is what makes bulk
|
|
1867
|
+
// invalidation possible without enumerating buckets. Single-slot tokens
|
|
1868
|
+
// are invalidated by deleting their one key.
|
|
1869
|
+
const epoch = deviceKey
|
|
1870
|
+
? await this.refreshTokenIdsStorage.getEpoch(user.id)
|
|
1871
|
+
: undefined;
|
|
1872
|
+
|
|
1773
1873
|
const refreshToken = await this.signToken(user.id, refreshTokenTtl, {
|
|
1774
1874
|
refreshTokenId,
|
|
1875
|
+
...(deviceKey ? { deviceKey } : {}),
|
|
1876
|
+
...(epoch !== undefined ? { epoch } : {}),
|
|
1775
1877
|
});
|
|
1776
1878
|
|
|
1777
1879
|
// store the refresh token id in the redis storage.
|
|
@@ -1779,6 +1881,7 @@ export class AuthenticationService {
|
|
|
1779
1881
|
user.id,
|
|
1780
1882
|
refreshToken,
|
|
1781
1883
|
previousRefreshToken,
|
|
1884
|
+
deviceKey,
|
|
1782
1885
|
);
|
|
1783
1886
|
|
|
1784
1887
|
return refreshToken;
|
|
@@ -1793,8 +1896,16 @@ export class AuthenticationService {
|
|
|
1793
1896
|
const issuer =
|
|
1794
1897
|
this.settingService.getConfigValue<SolidCoreSetting>("issuer");
|
|
1795
1898
|
|
|
1796
|
-
|
|
1797
|
-
|
|
1899
|
+
// deviceKey/epoch are absent on tokens issued before per-device sessions,
|
|
1900
|
+
// and on tokens issued while preventConcurrentLogins is on. That absence
|
|
1901
|
+
// is the signal the storage layer branches on - no request-body change
|
|
1902
|
+
// and no client change is needed to carry it.
|
|
1903
|
+
const { sub, deviceKey, epoch } = await this.jwtService.verifyAsync<
|
|
1904
|
+
Pick<ActiveUserData, "sub"> & {
|
|
1905
|
+
refreshTokenId: string;
|
|
1906
|
+
deviceKey?: string;
|
|
1907
|
+
epoch?: number;
|
|
1908
|
+
}
|
|
1798
1909
|
>(refreshTokenDto.refreshToken, {
|
|
1799
1910
|
secret,
|
|
1800
1911
|
audience,
|
|
@@ -1822,17 +1933,24 @@ export class AuthenticationService {
|
|
|
1822
1933
|
// throw new Error('Refresh token is invalid');
|
|
1823
1934
|
// }
|
|
1824
1935
|
|
|
1825
|
-
const
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1936
|
+
const rotated = await this.refreshTokenIdsStorage.validateAndRotate(
|
|
1937
|
+
user,
|
|
1938
|
+
refreshTokenDto.refreshToken,
|
|
1939
|
+
{ deviceKey, epoch },
|
|
1940
|
+
);
|
|
1830
1941
|
|
|
1831
1942
|
await this.userActivityHistoryService.logEvent("tokenRefreshed", user);
|
|
1832
1943
|
|
|
1833
1944
|
return {
|
|
1834
|
-
|
|
1835
|
-
|
|
1945
|
+
// The rotated key, not the incoming one: a session migrating out of the
|
|
1946
|
+
// pre-deploy single slot is assigned a bucket here, and the new access
|
|
1947
|
+
// token has to name the same one.
|
|
1948
|
+
accessToken: await this.generateAccessToken(
|
|
1949
|
+
user,
|
|
1950
|
+
undefined,
|
|
1951
|
+
rotated.deviceKey,
|
|
1952
|
+
),
|
|
1953
|
+
refreshToken: rotated.refreshToken,
|
|
1836
1954
|
};
|
|
1837
1955
|
} catch (err: any) {
|
|
1838
1956
|
if (err instanceof InvalidatedRefreshTokenError) {
|
|
@@ -2221,28 +2339,31 @@ export class AuthenticationService {
|
|
|
2221
2339
|
// // Invalidate the refresh token
|
|
2222
2340
|
// // await this.refreshTokenIdsStorage.invalidate(user.id);
|
|
2223
2341
|
// }
|
|
2224
|
-
async logout(refreshToken: string) {
|
|
2342
|
+
async logout(refreshToken: string, allDevices = false) {
|
|
2225
2343
|
try {
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
//
|
|
2229
|
-
//
|
|
2230
|
-
//
|
|
2231
|
-
//
|
|
2232
|
-
//
|
|
2233
|
-
//
|
|
2234
|
-
//
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
const payload = this.jwtService.decode(refreshToken) as any;
|
|
2239
|
-
|
|
2240
|
-
if (!payload || !payload.sub) {
|
|
2241
|
-
throw new UnauthorizedException(ERROR_MESSAGES.INVALID_REFRESH_TOKEN);
|
|
2344
|
+
const payload = await this.verifyRefreshTokenForLogout(refreshToken);
|
|
2345
|
+
|
|
2346
|
+
// Nothing verifiable to act on: forged, malformed, or signed with another
|
|
2347
|
+
// secret. Acting on an unauthenticated `sub` is exactly the hole this
|
|
2348
|
+
// closes - the route is @Public(), so a decoded-but-unverified token let
|
|
2349
|
+
// anyone log out an arbitrary user. Report success either way: logout
|
|
2350
|
+
// must be safe to call blindly from a client error path, and a 401/200
|
|
2351
|
+
// split here would leak whether a token string is genuine (RFC 7009 §2.2
|
|
2352
|
+
// takes the same position for revocation endpoints).
|
|
2353
|
+
if (!payload?.sub) {
|
|
2354
|
+
this.logger.warn("logout called with an unverifiable refresh token");
|
|
2355
|
+
return { message: SUCCESS_MESSAGES.LOGOUT_SUCCESS };
|
|
2242
2356
|
}
|
|
2243
2357
|
|
|
2244
2358
|
const userId = payload.sub;
|
|
2245
|
-
|
|
2359
|
+
if (allDevices) {
|
|
2360
|
+
await this.refreshTokenIdsStorage.invalidateAll(userId);
|
|
2361
|
+
} else {
|
|
2362
|
+
// Scoped to the bucket this token belongs to, so signing out on one
|
|
2363
|
+
// device leaves the others alive. A token with no deviceKey names the
|
|
2364
|
+
// single slot, which is the same key as before.
|
|
2365
|
+
await this.refreshTokenIdsStorage.invalidate(userId, payload.deviceKey);
|
|
2366
|
+
}
|
|
2246
2367
|
await this.activeSessionStorage.clearActiveSession(userId);
|
|
2247
2368
|
const user = await this.userRepository.findOne({
|
|
2248
2369
|
where: {
|
|
@@ -2250,10 +2371,15 @@ export class AuthenticationService {
|
|
|
2250
2371
|
},
|
|
2251
2372
|
});
|
|
2252
2373
|
// Log logout event
|
|
2253
|
-
|
|
2374
|
+
if (user) {
|
|
2375
|
+
await this.userActivityHistoryService.logEvent("logout", user);
|
|
2376
|
+
}
|
|
2254
2377
|
|
|
2255
2378
|
return { message: SUCCESS_MESSAGES.LOGOUT_SUCCESS };
|
|
2256
2379
|
} catch (err: any) {
|
|
2380
|
+
// JWT problems no longer reach here - verifyRefreshTokenForLogout
|
|
2381
|
+
// swallows them - so this is left for genuine infrastructure failures
|
|
2382
|
+
// (cache or database unavailable), where a 500 is the correct answer.
|
|
2257
2383
|
throw err instanceof UnauthorizedException ||
|
|
2258
2384
|
err instanceof InternalServerErrorException
|
|
2259
2385
|
? err
|
|
@@ -2261,6 +2387,31 @@ export class AuthenticationService {
|
|
|
2261
2387
|
}
|
|
2262
2388
|
}
|
|
2263
2389
|
|
|
2390
|
+
private async verifyRefreshTokenForLogout(
|
|
2391
|
+
refreshToken: string,
|
|
2392
|
+
): Promise<{ sub: number; deviceKey?: string } | null> {
|
|
2393
|
+
try {
|
|
2394
|
+
const payload = await this.jwtService.verifyAsync(refreshToken, {
|
|
2395
|
+
secret: this.settingService.getConfigValue<SolidCoreSetting>("secret"),
|
|
2396
|
+
audience:
|
|
2397
|
+
this.settingService.getConfigValue<SolidCoreSetting>("audience"),
|
|
2398
|
+
issuer: this.settingService.getConfigValue<SolidCoreSetting>("issuer"),
|
|
2399
|
+
// Signature, audience and issuer are still enforced; only `exp` is
|
|
2400
|
+
// tolerated. An expired session must still be able to log out - the
|
|
2401
|
+
// previous jwtService.decode() allowed that, and a plain verifyAsync
|
|
2402
|
+
// would throw TokenExpiredError, which the catch above would turn into
|
|
2403
|
+
// a 500 on an ordinary sign-out.
|
|
2404
|
+
ignoreExpiration: true,
|
|
2405
|
+
});
|
|
2406
|
+
|
|
2407
|
+
// Access and refresh tokens share secret, audience and issuer, so only
|
|
2408
|
+
// this claim distinguishes them. Reject an access token presented here.
|
|
2409
|
+
return payload?.refreshTokenId ? payload : null;
|
|
2410
|
+
} catch {
|
|
2411
|
+
return null; // bad signature or malformed
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
|
|
2264
2415
|
async activateUser(userId: number) {
|
|
2265
2416
|
const user = await this.userService.findOne(userId, {});
|
|
2266
2417
|
if (!user) {
|
|
@@ -2282,9 +2433,14 @@ export class AuthenticationService {
|
|
|
2282
2433
|
|
|
2283
2434
|
// const tokens = await this.generateTokens(user);
|
|
2284
2435
|
|
|
2285
|
-
// Get the refresh token for a user from refresh token storage.
|
|
2436
|
+
// Get the refresh token for a user from refresh token storage. The bucket
|
|
2437
|
+
// is named by the access token's own deviceKey claim; absent for
|
|
2438
|
+
// single-slot sessions, which resolves to the same key as before.
|
|
2286
2439
|
const refreshTokenState =
|
|
2287
|
-
await this.refreshTokenIdsStorage.getCurrentRefreshTokenState(
|
|
2440
|
+
await this.refreshTokenIdsStorage.getCurrentRefreshTokenState(
|
|
2441
|
+
user.id,
|
|
2442
|
+
activeUser.deviceKey,
|
|
2443
|
+
);
|
|
2288
2444
|
|
|
2289
2445
|
const response = {
|
|
2290
2446
|
user: {
|
|
@@ -2295,7 +2451,11 @@ export class AuthenticationService {
|
|
|
2295
2451
|
id: user.id,
|
|
2296
2452
|
roles: user.roles.map((role) => role.name),
|
|
2297
2453
|
},
|
|
2298
|
-
|
|
2454
|
+
// Null-guarded because the cache entry now carries a TTL: once it
|
|
2455
|
+
// expires - or after a logout followed by a call with a still-valid
|
|
2456
|
+
// access token - there is no state to read. Matches the existing
|
|
2457
|
+
// handling in generateSsoCode.
|
|
2458
|
+
refreshToken: refreshTokenState?.currentRefreshToken ?? null,
|
|
2299
2459
|
// ...tokens
|
|
2300
2460
|
};
|
|
2301
2461
|
return response;
|
|
@@ -2308,6 +2468,7 @@ export class AuthenticationService {
|
|
|
2308
2468
|
const refreshTokenState =
|
|
2309
2469
|
await this.refreshTokenIdsStorage.getCurrentRefreshTokenState(
|
|
2310
2470
|
activeUser.sub,
|
|
2471
|
+
activeUser.deviceKey,
|
|
2311
2472
|
);
|
|
2312
2473
|
if (!refreshTokenState?.currentRefreshToken) {
|
|
2313
2474
|
throw new UnauthorizedException("No active session found");
|
|
@@ -2342,6 +2503,7 @@ function parseUniqueConstraintError(detail: string): string {
|
|
|
2342
2503
|
const fieldMap: Record<string, string> = {
|
|
2343
2504
|
username: "username",
|
|
2344
2505
|
email: "email address",
|
|
2506
|
+
mobile: "mobile number",
|
|
2345
2507
|
full_name_user_key: "full name",
|
|
2346
2508
|
};
|
|
2347
2509
|
const friendlyField = fieldMap[field] || field;
|
|
@@ -5,7 +5,8 @@ import { InjectEntityManager } from '@nestjs/typeorm';
|
|
|
5
5
|
import { Brackets, EntityManager, EntityMetadata, In } from 'typeorm';
|
|
6
6
|
|
|
7
7
|
import { classify } from '@angular-devkit/core/src/utils/strings';
|
|
8
|
-
import { CHATTER_MESSAGE_STATUS, CHATTER_MESSAGE_SUBTYPE, CHATTER_MESSAGE_TYPE } from 'src/constants/chatter-message.constants';
|
|
8
|
+
import { CHATTER_MESSAGE_STATUS, CHATTER_MESSAGE_SUBTYPE, CHATTER_MESSAGE_TYPE, CHATTER_MESSAGE_USER_FIELDS } from 'src/constants/chatter-message.constants';
|
|
9
|
+
import { isDangerousMediaFile } from 'src/constants/media-file-types';
|
|
9
10
|
import { ERROR_MESSAGES } from 'src/constants/error-messages';
|
|
10
11
|
import { PostChatterMessageDto } from 'src/dtos/post-chatter-message.dto';
|
|
11
12
|
import { UpdateChatterNoteMessageDto } from 'src/dtos/update-chatter-note-message.dto';
|
|
@@ -18,9 +19,11 @@ import { FieldMetadataRepository } from 'src/repository/field-metadata.repositor
|
|
|
18
19
|
import { MediaRepository } from 'src/repository/media.repository';
|
|
19
20
|
import { ModelMetadataRepository } from 'src/repository/model-metadata.repository';
|
|
20
21
|
import { CRUDService } from 'src/services/crud.service';
|
|
22
|
+
import { BasicFilterDto } from '../dtos/basic-filters.dto';
|
|
21
23
|
import { MediaStorageProviderType } from '../dtos/create-media-storage-provider-metadata.dto';
|
|
22
24
|
import { ChatterMessageDetails } from '../entities/chatter-message-details.entity';
|
|
23
25
|
import { ChatterMessage } from '../entities/chatter-message.entity';
|
|
26
|
+
import { User } from '../entities/user.entity';
|
|
24
27
|
import { getMediaStorageProvider } from './mediaStorageProviders';
|
|
25
28
|
import { RequestContextService } from './request-context.service';
|
|
26
29
|
import { Logger } from '@nestjs/common';
|
|
@@ -34,6 +37,16 @@ interface ChatterMention {
|
|
|
34
37
|
id?: string | number;
|
|
35
38
|
}
|
|
36
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Server-side-only overrides for `postMessage`. Not part of `PostChatterMessageDto`, so
|
|
42
|
+
* nothing here is settable over HTTP - only in-process callers such as migration or
|
|
43
|
+
* back-fill services can supply it.
|
|
44
|
+
*/
|
|
45
|
+
export interface PostChatterMessageOptions {
|
|
46
|
+
/** Backdate the message instead of stamping it with the current time. */
|
|
47
|
+
createdAt?: Date;
|
|
48
|
+
}
|
|
49
|
+
|
|
37
50
|
@Injectable()
|
|
38
51
|
export class ChatterMessageService extends CRUDService<ChatterMessage> {
|
|
39
52
|
private readonly _logger = new Logger(ChatterMessageService.name);
|
|
@@ -101,6 +114,44 @@ export class ChatterMessageService extends CRUDService<ChatterMessage> {
|
|
|
101
114
|
chatterMessage.updatedBy = resolvedUserId;
|
|
102
115
|
}
|
|
103
116
|
|
|
117
|
+
/**
|
|
118
|
+
* Reduce a message's hydrated `user` relation to CHATTER_MESSAGE_USER_FIELDS.
|
|
119
|
+
*
|
|
120
|
+
* `getChatterMessages` restricts the columns in the query itself, which is preferable.
|
|
121
|
+
* The generic CRUD paths below build their query from find-options and hand `populate`
|
|
122
|
+
* straight to TypeORM, so they are trimmed after the fact instead. (`createdBy` /
|
|
123
|
+
* `updatedBy` need nothing here - CRUDService.handlePopulateUserIdFields already selects
|
|
124
|
+
* only USER_SUMMARY_FIELDS when a caller populates them.)
|
|
125
|
+
*/
|
|
126
|
+
private trimMessageUser<M extends ChatterMessage>(message: M): M {
|
|
127
|
+
if (message?.user) {
|
|
128
|
+
message.user = this.toUserSummary(message.user) as User;
|
|
129
|
+
}
|
|
130
|
+
return message;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private trimMessageUsers(messages: ChatterMessage[] | undefined) {
|
|
134
|
+
messages?.forEach(message => this.trimMessageUser(message));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* `GET /chatter-message?populate[]=user` would otherwise be a way around the column
|
|
139
|
+
* allowlist applied by getChatterMessages.
|
|
140
|
+
*/
|
|
141
|
+
async find(basicFilterDto: BasicFilterDto, solidRequestContext: any = {}): Promise<any> {
|
|
142
|
+
const result = await super.find(basicFilterDto, solidRequestContext);
|
|
143
|
+
this.trimMessageUsers(result?.records);
|
|
144
|
+
// A grouped find (populateGroup) nests its entities one level deeper.
|
|
145
|
+
for (const groupRecord of result?.groupRecords ?? []) {
|
|
146
|
+
this.trimMessageUsers(groupRecord?.groupData?.records);
|
|
147
|
+
}
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async findOne(id: number, query: any = {}, solidRequestContext: any = {}) {
|
|
152
|
+
return this.trimMessageUser(await super.findOne(id, query, solidRequestContext));
|
|
153
|
+
}
|
|
154
|
+
|
|
104
155
|
private isEditableCustomNoteMessage(message: ChatterMessage): boolean {
|
|
105
156
|
if (message.messageType !== CHATTER_MESSAGE_TYPE.CUSTOM) {
|
|
106
157
|
return false;
|
|
@@ -134,6 +185,17 @@ export class ChatterMessageService extends CRUDService<ChatterMessage> {
|
|
|
134
185
|
}, messageBody || '');
|
|
135
186
|
}
|
|
136
187
|
|
|
188
|
+
/**
|
|
189
|
+
* Chatter attachments bypass MediaFieldCrudManager - they go straight to
|
|
190
|
+
* storageProvider.store() - so the shared predicate is applied here directly. The rules
|
|
191
|
+
* themselves live in media-file-types so upload paths can't drift apart.
|
|
192
|
+
*/
|
|
193
|
+
private validateChatterMediaFiles(files: Express.Multer.File[] = []) {
|
|
194
|
+
if (Array.isArray(files) && files.some(file => isDangerousMediaFile(file))) {
|
|
195
|
+
throw new BadRequestException('Dangerous file types are not allowed in chatter attachments.');
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
137
199
|
private async publishChatterMentionNotifications(message: ChatterMessage, model: any) {
|
|
138
200
|
if (message.messageType !== CHATTER_MESSAGE_TYPE.CUSTOM || message.messageSubType !== CHATTER_MESSAGE_SUBTYPE.NOTE) {
|
|
139
201
|
return;
|
|
@@ -188,6 +250,31 @@ export class ChatterMessageService extends CRUDService<ChatterMessage> {
|
|
|
188
250
|
}
|
|
189
251
|
}
|
|
190
252
|
|
|
253
|
+
// Deliberately bypasses UserRepository's security-rule filtering: the mention picker
|
|
254
|
+
// needs id/username/fullName for any active user regardless of the caller's row-level
|
|
255
|
+
// access to the User model, same as the getChatterMessages one-to-many lookup below.
|
|
256
|
+
async getMentionableUsers(search?: string, limit: number = 8): Promise<Array<{ id: number; username: string; fullName: string }>> {
|
|
257
|
+
const normalizedLimit = Number.isInteger(limit) && limit > 0 && limit <= 50 ? limit : 8;
|
|
258
|
+
const userRepository = this.entityManager.getRepository(User);
|
|
259
|
+
|
|
260
|
+
const qb = userRepository
|
|
261
|
+
.createQueryBuilder('user')
|
|
262
|
+
.select(['user.id', 'user.username', 'user.fullName'])
|
|
263
|
+
.where('user.active = :active', { active: true });
|
|
264
|
+
|
|
265
|
+
const trimmedSearch = (search ?? '').trim();
|
|
266
|
+
if (trimmedSearch) {
|
|
267
|
+
qb.andWhere('(LOWER(user.username) LIKE :search OR LOWER(user.fullName) LIKE :search)', {
|
|
268
|
+
search: `%${trimmedSearch.toLowerCase()}%`,
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
qb.orderBy('user.username', 'ASC').take(normalizedLimit);
|
|
273
|
+
|
|
274
|
+
const users = await qb.getMany();
|
|
275
|
+
return users.map(user => ({ id: user.id, username: user.username, fullName: user.fullName }));
|
|
276
|
+
}
|
|
277
|
+
|
|
191
278
|
async markCompleted(id: number) {
|
|
192
279
|
const activeUser = this.requestContextService.getActiveUser();
|
|
193
280
|
if (!activeUser) {
|
|
@@ -240,6 +327,8 @@ export class ChatterMessageService extends CRUDService<ChatterMessage> {
|
|
|
240
327
|
throw new BadRequestException('Message body cannot be empty.');
|
|
241
328
|
}
|
|
242
329
|
|
|
330
|
+
this.validateChatterMediaFiles(files);
|
|
331
|
+
|
|
243
332
|
if (hasMessageBody) {
|
|
244
333
|
message.messageBody = trimmedMessageBody;
|
|
245
334
|
}
|
|
@@ -306,13 +395,23 @@ export class ChatterMessageService extends CRUDService<ChatterMessage> {
|
|
|
306
395
|
|
|
307
396
|
await this.publishChatterMentionNotifications(savedMessage, model);
|
|
308
397
|
|
|
309
|
-
return
|
|
398
|
+
// The `user` relation was loaded for the ownership check above; don't return all of it.
|
|
399
|
+
return this.trimMessageUser(savedMessage);
|
|
310
400
|
}
|
|
311
401
|
|
|
312
|
-
|
|
402
|
+
/**
|
|
403
|
+
* Post a chatter message.
|
|
404
|
+
*
|
|
405
|
+
* `options` is deliberately kept out of `PostChatterMessageDto` so it can only be supplied
|
|
406
|
+
* by server-side callers (migrations, back-fills). The controller binds the DTO alone, so
|
|
407
|
+
* an API client has no way to backdate a message.
|
|
408
|
+
*/
|
|
409
|
+
async postMessage(postDto: PostChatterMessageDto, files: Express.Multer.File[] = [], options: PostChatterMessageOptions = {}) {
|
|
313
410
|
const coModelName = lowerFirst(postDto.coModelName);
|
|
314
411
|
await this.assertRecordAccess(coModelName, postDto.coModelEntityId);
|
|
315
412
|
|
|
413
|
+
this.validateChatterMediaFiles(files);
|
|
414
|
+
|
|
316
415
|
const chatterMessage = new ChatterMessage();
|
|
317
416
|
chatterMessage.messageType = CHATTER_MESSAGE_TYPE.CUSTOM;
|
|
318
417
|
chatterMessage.messageSubType = postDto.messageSubType || CHATTER_MESSAGE_SUBTYPE.CUSTOM;
|
|
@@ -322,6 +421,10 @@ export class ChatterMessageService extends CRUDService<ChatterMessage> {
|
|
|
322
421
|
chatterMessage.coModelEntityId = postDto.coModelEntityId;
|
|
323
422
|
chatterMessage.coModelName = coModelName;
|
|
324
423
|
chatterMessage.modelUserKey = postDto.modelUserKey ?? null;
|
|
424
|
+
// Left unset otherwise, so the @CreateDateColumn default still applies.
|
|
425
|
+
if (options.createdAt) {
|
|
426
|
+
chatterMessage.createdAt = options.createdAt;
|
|
427
|
+
}
|
|
325
428
|
|
|
326
429
|
const model = await this.modelMetadataRepo.findOne({
|
|
327
430
|
where: { singularName: coModelName },
|
|
@@ -891,16 +994,25 @@ export class ChatterMessageService extends CRUDService<ChatterMessage> {
|
|
|
891
994
|
qb.where(orConditions.join(' OR '), parameters);
|
|
892
995
|
}));
|
|
893
996
|
|
|
894
|
-
const relations = ['chatterMessageDetails'
|
|
997
|
+
const relations = ['chatterMessageDetails'];
|
|
895
998
|
if (populate && populate.length > 0) {
|
|
896
999
|
const normalizedPopulate = this.crudHelperService.normalize(populate);
|
|
897
|
-
|
|
1000
|
+
// SECURITY: 'user' is joined below with an explicit column allowlist. A client-supplied
|
|
1001
|
+
// populate[]=user must not be able to turn it back into a full-entity join (the chatter
|
|
1002
|
+
// panel does send exactly that).
|
|
1003
|
+
relations.push(...normalizedPopulate.filter(rel => rel !== 'user' && !relations.includes(rel)));
|
|
898
1004
|
}
|
|
899
1005
|
|
|
900
1006
|
relations.forEach(relation => {
|
|
901
1007
|
qb.leftJoinAndSelect(`entity.${relation}`, relation);
|
|
902
1008
|
});
|
|
903
1009
|
|
|
1010
|
+
// Only the author's id and display name leave this endpoint - see CHATTER_MESSAGE_USER_FIELDS.
|
|
1011
|
+
// This join must stay before applyFilters below: it lets filters[user][fullName] reuse this
|
|
1012
|
+
// alias instead of adding a second join of its own.
|
|
1013
|
+
qb.leftJoin('entity.user', 'user');
|
|
1014
|
+
qb.addSelect(CHATTER_MESSAGE_USER_FIELDS.map(field => `user.${field}`));
|
|
1015
|
+
|
|
904
1016
|
if (filters) {
|
|
905
1017
|
qb.andWhere(new Brackets(whereQb => {
|
|
906
1018
|
this.crudHelperService.applyFilters(whereQb, filters, 'entity', qb);
|
|
@@ -914,6 +1026,13 @@ export class ChatterMessageService extends CRUDService<ChatterMessage> {
|
|
|
914
1026
|
const [entities, count] = await qb.getManyAndCount();
|
|
915
1027
|
this.logHeapUsed('getChatterMessages-entitiesLoaded');
|
|
916
1028
|
|
|
1029
|
+
// The join above only selects the allowlisted columns, but TypeORM still hydrates the
|
|
1030
|
+
// author with `new User()`, which leaves every initialiser-backed property (active,
|
|
1031
|
+
// forcePasswordChange, ...) sitting on the instance holding its default rather than a
|
|
1032
|
+
// value read from the database. Replace it with a plain object of exactly the
|
|
1033
|
+
// allowlisted keys so the response cannot report fabricated defaults as data.
|
|
1034
|
+
this.trimMessageUsers(entities);
|
|
1035
|
+
|
|
917
1036
|
// Convert date strings in message details to ISO format for consistent handling on the frontend
|
|
918
1037
|
const DATE_FIELD_TYPES = ['date', 'datetime', 'time'];
|
|
919
1038
|
for (const entity of entities) {
|