@acorex-platform/module-security-management-server 0.0.18 → 2.0.0-next.42
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/contracts/src/AXM_USER_ACCESS_STATE_DEFINITION_KEY.js.map +1 -1
- package/contracts/src/AXM_USER_EFFECTIVE_STATE_DEFINITION_KEY.js.map +1 -1
- package/contracts/src/ROLE_PERMISSIONS_PAGE_COMPONENT_KEY.d.ts +2 -0
- package/contracts/src/ROLE_PERMISSIONS_PAGE_COMPONENT_KEY.js +6 -0
- package/contracts/src/ROLE_PERMISSIONS_PAGE_COMPONENT_KEY.js.map +1 -0
- package/contracts/src/USER_ROLES_PAGE_COMPONENT_KEY.d.ts +1 -0
- package/contracts/src/USER_ROLES_PAGE_COMPONENT_KEY.js +5 -0
- package/contracts/src/USER_ROLES_PAGE_COMPONENT_KEY.js.map +1 -0
- package/contracts/src/create-application-role-rows.util.d.ts +15 -0
- package/contracts/src/create-application-role-rows.util.js +67 -0
- package/contracts/src/create-application-role-rows.util.js.map +1 -0
- package/contracts/src/device-session-admin-scope.d.ts +15 -0
- package/contracts/src/device-session-admin-scope.js +16 -0
- package/contracts/src/device-session-admin-scope.js.map +1 -0
- package/contracts/src/entities/device-sessions/device-sessions.entity.js +50 -1
- package/contracts/src/entities/device-sessions/device-sessions.entity.js.map +1 -1
- package/contracts/src/entities/device-sessions/device-sessions.types.d.ts +7 -0
- package/contracts/src/entities/device-sessions/device-sessions.types.js.map +1 -1
- package/contracts/src/entities/device-sessions/index.d.ts +0 -1
- package/contracts/src/entities/device-sessions/index.js +0 -1
- package/contracts/src/entities/device-sessions/index.js.map +1 -1
- package/contracts/src/entities/identity-provider/identity-provider.entity.d.ts +2 -0
- package/contracts/src/entities/identity-provider/identity-provider.entity.js +453 -0
- package/contracts/src/entities/identity-provider/identity-provider.entity.js.map +1 -0
- package/contracts/src/entities/identity-provider/identity-provider.types.d.ts +61 -0
- package/contracts/src/entities/identity-provider/identity-provider.types.js +14 -0
- package/contracts/src/entities/identity-provider/identity-provider.types.js.map +1 -0
- package/contracts/src/entities/identity-provider/index.d.ts +1 -0
- package/contracts/src/entities/identity-provider/index.js +5 -0
- package/contracts/src/entities/identity-provider/index.js.map +1 -0
- package/contracts/src/entities/index.d.ts +4 -0
- package/contracts/src/entities/index.js +4 -0
- package/contracts/src/entities/index.js.map +1 -1
- package/contracts/src/entities/policy/index.d.ts +0 -1
- package/contracts/src/entities/policy/index.js +0 -1
- package/contracts/src/entities/policy/index.js.map +1 -1
- package/contracts/src/entities/policy/policy.entity.js +15 -0
- package/contracts/src/entities/policy/policy.entity.js.map +1 -1
- package/contracts/src/entities/policy/policy.types.js.map +1 -1
- package/contracts/src/entities/profile/index.d.ts +0 -1
- package/contracts/src/entities/profile/index.js +0 -1
- package/contracts/src/entities/profile/index.js.map +1 -1
- package/contracts/src/entities/profile/profile.entity.js +15 -0
- package/contracts/src/entities/profile/profile.entity.js.map +1 -1
- package/contracts/src/entities/profile/profile.types.js.map +1 -1
- package/contracts/src/entities/role-permission/index.d.ts +1 -0
- package/contracts/src/entities/role-permission/index.js +5 -0
- package/contracts/src/entities/role-permission/index.js.map +1 -0
- package/contracts/src/entities/role-permission/role-permission.entity.d.ts +2 -0
- package/contracts/src/entities/role-permission/role-permission.entity.js +163 -0
- package/contracts/src/entities/role-permission/role-permission.entity.js.map +1 -0
- package/contracts/src/entities/role-permission/role-permission.types.d.ts +11 -0
- package/contracts/src/entities/role-permission/role-permission.types.js +5 -0
- package/contracts/src/entities/role-permission/role-permission.types.js.map +1 -0
- package/contracts/src/entities/roles/index.d.ts +0 -1
- package/contracts/src/entities/roles/index.js +0 -1
- package/contracts/src/entities/roles/index.js.map +1 -1
- package/contracts/src/entities/roles/roles.entity.js +73 -144
- package/contracts/src/entities/roles/roles.entity.js.map +1 -1
- package/contracts/src/entities/roles/roles.types.d.ts +4 -1
- package/contracts/src/entities/roles/roles.types.js.map +1 -1
- package/contracts/src/entities/user-role/index.d.ts +1 -0
- package/{src/lib/seed/tenant → contracts/src/entities/user-role}/index.js +1 -1
- package/contracts/src/entities/user-role/index.js.map +1 -0
- package/contracts/src/entities/user-role/user-role.entity.d.ts +2 -0
- package/contracts/src/entities/user-role/user-role.entity.js +164 -0
- package/contracts/src/entities/user-role/user-role.entity.js.map +1 -0
- package/contracts/src/entities/user-role/user-role.types.d.ts +11 -0
- package/contracts/src/entities/user-role/user-role.types.js +5 -0
- package/contracts/src/entities/user-role/user-role.types.js.map +1 -0
- package/contracts/src/entities/user-two-factor/index.d.ts +2 -0
- package/contracts/src/entities/user-two-factor/index.js +8 -0
- package/contracts/src/entities/user-two-factor/index.js.map +1 -0
- package/contracts/src/entities/user-two-factor/user-two-factor.entity.d.ts +6 -0
- package/contracts/src/entities/user-two-factor/user-two-factor.entity.js +103 -0
- package/contracts/src/entities/user-two-factor/user-two-factor.entity.js.map +1 -0
- package/contracts/src/entities/user-two-factor/user-two-factor.types.d.ts +24 -0
- package/contracts/src/entities/user-two-factor/user-two-factor.types.js +4 -0
- package/contracts/src/entities/user-two-factor/user-two-factor.types.js.map +1 -0
- package/contracts/src/entities/users/index.d.ts +1 -1
- package/contracts/src/entities/users/index.js +1 -1
- package/contracts/src/entities/users/index.js.map +1 -1
- package/contracts/src/entities/users/user-provisioning-list-actions.d.ts +8 -0
- package/contracts/src/entities/users/user-provisioning-list-actions.js +95 -0
- package/contracts/src/entities/users/user-provisioning-list-actions.js.map +1 -0
- package/contracts/src/entities/users/users.entity.js +244 -83
- package/contracts/src/entities/users/users.entity.js.map +1 -1
- package/contracts/src/entities/users/users.types.d.ts +15 -0
- package/contracts/src/entities/users/users.types.js.map +1 -1
- package/contracts/src/entity-catalog.d.ts +7 -0
- package/contracts/src/entity-catalog.js +28 -0
- package/contracts/src/entity-catalog.js.map +1 -0
- package/contracts/src/entity-entry.device-sessions.d.ts +1 -0
- package/contracts/src/entity-entry.device-sessions.js +6 -0
- package/contracts/src/entity-entry.device-sessions.js.map +1 -0
- package/contracts/src/entity-entry.identity-provider.d.ts +1 -0
- package/contracts/src/entity-entry.identity-provider.js +6 -0
- package/contracts/src/entity-entry.identity-provider.js.map +1 -0
- package/contracts/src/entity-entry.policy.d.ts +1 -0
- package/contracts/src/entity-entry.policy.js +6 -0
- package/contracts/src/entity-entry.policy.js.map +1 -0
- package/contracts/src/entity-entry.profile.d.ts +1 -0
- package/contracts/src/entity-entry.profile.js +6 -0
- package/contracts/src/entity-entry.profile.js.map +1 -0
- package/contracts/src/entity-entry.role-permission.d.ts +1 -0
- package/contracts/src/entity-entry.role-permission.js +6 -0
- package/contracts/src/entity-entry.role-permission.js.map +1 -0
- package/contracts/src/entity-entry.roles.d.ts +1 -0
- package/contracts/src/entity-entry.roles.js +6 -0
- package/contracts/src/entity-entry.roles.js.map +1 -0
- package/contracts/src/entity-entry.user-role.d.ts +1 -0
- package/contracts/src/entity-entry.user-role.js +6 -0
- package/contracts/src/entity-entry.user-role.js.map +1 -0
- package/contracts/src/entity-entry.user-two-factor.d.ts +1 -0
- package/contracts/src/entity-entry.user-two-factor.js +6 -0
- package/contracts/src/entity-entry.user-two-factor.js.map +1 -0
- package/contracts/src/entity-entry.users.d.ts +1 -0
- package/contracts/src/entity-entry.users.js +6 -0
- package/contracts/src/entity-entry.users.js.map +1 -0
- package/contracts/src/features.keys.d.ts +4 -0
- package/contracts/src/features.keys.js +4 -0
- package/contracts/src/features.keys.js.map +1 -1
- package/contracts/src/index.d.ts +10 -1
- package/contracts/src/index.js +10 -1
- package/contracts/src/index.js.map +1 -1
- package/contracts/src/invite-application-role-rows.util.d.ts +16 -0
- package/contracts/src/invite-application-role-rows.util.js +68 -0
- package/contracts/src/invite-application-role-rows.util.js.map +1 -0
- package/contracts/src/invite-assignable-roles.util.d.ts +34 -0
- package/contracts/src/invite-assignable-roles.util.js +75 -0
- package/contracts/src/invite-assignable-roles.util.js.map +1 -0
- package/contracts/src/lib/context.definitions.d.ts +2 -0
- package/contracts/src/lib/context.definitions.js +15 -0
- package/contracts/src/lib/context.definitions.js.map +1 -0
- package/contracts/src/manifest.d.ts +6 -0
- package/contracts/src/manifest.js +24 -0
- package/contracts/src/manifest.js.map +1 -0
- package/contracts/src/menu.keys.d.ts +1 -0
- package/contracts/src/menu.keys.js +1 -0
- package/contracts/src/menu.keys.js.map +1 -1
- package/contracts/src/permission.keys.d.ts +48 -18
- package/contracts/src/permission.keys.js +49 -21
- package/contracts/src/permission.keys.js.map +1 -1
- package/contracts/src/resolve-user-effective-state.js.map +1 -1
- package/contracts/src/root-config.d.ts +33 -0
- package/contracts/src/root-config.js +33 -0
- package/contracts/src/root-config.js.map +1 -1
- package/contracts/src/server-commands.d.ts +322 -0
- package/contracts/src/server-commands.js +77 -1
- package/contracts/src/server-commands.js.map +1 -1
- package/contracts/src/settings.keys.d.ts +16 -0
- package/contracts/src/settings.keys.js +21 -0
- package/contracts/src/settings.keys.js.map +1 -0
- package/contracts/src/user-access-state.js.map +1 -1
- package/contracts/src/user-effective-state.js.map +1 -1
- package/contracts/src/user-invitation.d.ts +41 -0
- package/contracts/src/user-invitation.js +18 -0
- package/contracts/src/user-invitation.js.map +1 -0
- package/contracts/src/user-password-reset.js.map +1 -1
- package/package.json +16 -18
- package/server/src/index.d.ts +3 -0
- package/server/src/index.js +11 -0
- package/server/src/index.js.map +1 -0
- package/server/src/lib/effective-access-history/effective-access-history.service.d.ts +35 -0
- package/server/src/lib/effective-access-history/effective-access-history.service.js +486 -0
- package/server/src/lib/effective-access-history/effective-access-history.service.js.map +1 -0
- package/server/src/lib/effective-access-history/effective-access-history.types.d.ts +64 -0
- package/server/src/lib/effective-access-history/effective-access-history.types.js +5 -0
- package/server/src/lib/effective-access-history/effective-access-history.types.js.map +1 -0
- package/server/src/lib/effective-access-history/version-as-of.util.d.ts +37 -0
- package/server/src/lib/effective-access-history/version-as-of.util.js +167 -0
- package/server/src/lib/effective-access-history/version-as-of.util.js.map +1 -0
- package/server/src/lib/entities/index.d.ts +1 -0
- package/server/src/lib/entities/index.js +5 -0
- package/{src/lib/seed/tenant → server/src/lib/entities}/index.js.map +1 -1
- package/server/src/lib/entities/security-management.entity-catalog.d.ts +1 -0
- package/server/src/lib/entities/security-management.entity-catalog.js +12 -0
- package/server/src/lib/entities/security-management.entity-catalog.js.map +1 -0
- package/server/src/lib/handlers/security-report.handlers.d.ts +36 -0
- package/server/src/lib/handlers/security-report.handlers.js +164 -0
- package/server/src/lib/handlers/security-report.handlers.js.map +1 -0
- package/server/src/lib/identity-provider/auth-oauth-clients.constants.d.ts +5 -0
- package/server/src/lib/identity-provider/auth-oauth-clients.constants.js +11 -0
- package/server/src/lib/identity-provider/auth-oauth-clients.constants.js.map +1 -0
- package/server/src/lib/identity-provider/identity-provider-catalog.service.d.ts +20 -0
- package/server/src/lib/identity-provider/identity-provider-catalog.service.js +169 -0
- package/server/src/lib/identity-provider/identity-provider-catalog.service.js.map +1 -0
- package/server/src/lib/identity-provider/identity-provider-ids.constants.d.ts +8 -0
- package/server/src/lib/identity-provider/identity-provider-ids.constants.js +19 -0
- package/server/src/lib/identity-provider/identity-provider-ids.constants.js.map +1 -0
- package/server/src/lib/identity-provider/identity-provider-stub.bootstrap.d.ts +12 -0
- package/server/src/lib/identity-provider/identity-provider-stub.bootstrap.js +48 -0
- package/server/src/lib/identity-provider/identity-provider-stub.bootstrap.js.map +1 -0
- package/server/src/lib/identity-provider/identity-provider.constants.d.ts +1 -0
- package/server/src/lib/identity-provider/identity-provider.constants.js +7 -0
- package/server/src/lib/identity-provider/identity-provider.constants.js.map +1 -0
- package/server/src/lib/middlewares/device-sessions-active.middleware.d.ts +15 -0
- package/server/src/lib/middlewares/device-sessions-active.middleware.js +76 -0
- package/server/src/lib/middlewares/device-sessions-active.middleware.js.map +1 -0
- package/server/src/lib/middlewares/device-sessions-app-scope.middleware.d.ts +29 -0
- package/server/src/lib/middlewares/device-sessions-app-scope.middleware.js +137 -0
- package/server/src/lib/middlewares/device-sessions-app-scope.middleware.js.map +1 -0
- package/server/src/lib/middlewares/device-sessions-app-scope.util.d.ts +41 -0
- package/server/src/lib/middlewares/device-sessions-app-scope.util.js +69 -0
- package/server/src/lib/middlewares/device-sessions-app-scope.util.js.map +1 -0
- package/server/src/lib/middlewares/device-sessions-token-revoke.middleware.d.ts +18 -0
- package/server/src/lib/middlewares/device-sessions-token-revoke.middleware.js +90 -0
- package/server/src/lib/middlewares/device-sessions-token-revoke.middleware.js.map +1 -0
- package/server/src/lib/middlewares/user-assigned-roles.middleware.d.ts +36 -0
- package/server/src/lib/middlewares/user-assigned-roles.middleware.js +359 -0
- package/server/src/lib/middlewares/user-assigned-roles.middleware.js.map +1 -0
- package/{src → server/src}/lib/middlewares/user-effective-state.middleware.js +1 -1
- package/server/src/lib/middlewares/user-effective-state.middleware.js.map +1 -0
- package/server/src/lib/middlewares/user-invitation-list.middleware.d.ts +32 -0
- package/server/src/lib/middlewares/user-invitation-list.middleware.js +250 -0
- package/server/src/lib/middlewares/user-invitation-list.middleware.js.map +1 -0
- package/server/src/lib/middlewares/user-profile-write.middleware.d.ts +18 -0
- package/server/src/lib/middlewares/user-profile-write.middleware.js +206 -0
- package/server/src/lib/middlewares/user-profile-write.middleware.js.map +1 -0
- package/server/src/lib/password-reset/change-own-password.command.d.ts +8 -0
- package/server/src/lib/password-reset/change-own-password.command.js +38 -0
- package/server/src/lib/password-reset/change-own-password.command.js.map +1 -0
- package/{src → server/src}/lib/password-reset/complete-password-reset.command.d.ts +3 -4
- package/{src → server/src}/lib/password-reset/complete-password-reset.command.js +8 -12
- package/server/src/lib/password-reset/complete-password-reset.command.js.map +1 -0
- package/server/src/lib/password-reset/get-own-password-status.command.d.ts +8 -0
- package/server/src/lib/password-reset/get-own-password-status.command.js +31 -0
- package/server/src/lib/password-reset/get-own-password-status.command.js.map +1 -0
- package/{src → server/src}/lib/password-reset/send-password-reset-email.command.d.ts +12 -13
- package/{src → server/src}/lib/password-reset/send-password-reset-email.command.js +10 -15
- package/server/src/lib/password-reset/send-password-reset-email.command.js.map +1 -0
- package/server/src/lib/password-reset/user-change-password.service.d.ts +24 -0
- package/server/src/lib/password-reset/user-change-password.service.js +92 -0
- package/server/src/lib/password-reset/user-change-password.service.js.map +1 -0
- package/{src → server/src}/lib/password-reset/user-password-reset.service.d.ts +2 -7
- package/{src → server/src}/lib/password-reset/user-password-reset.service.js +29 -90
- package/server/src/lib/password-reset/user-password-reset.service.js.map +1 -0
- package/server/src/lib/queries/get-permission-holders-history.query.d.ts +4 -0
- package/server/src/lib/queries/get-permission-holders-history.query.definition.d.ts +5 -0
- package/server/src/lib/queries/get-permission-holders-history.query.definition.js +38 -0
- package/server/src/lib/queries/get-permission-holders-history.query.definition.js.map +1 -0
- package/server/src/lib/queries/get-permission-holders-history.query.js +25 -0
- package/server/src/lib/queries/get-permission-holders-history.query.js.map +1 -0
- package/server/src/lib/queries/get-role-holders-history.query.d.ts +4 -0
- package/server/src/lib/queries/get-role-holders-history.query.definition.d.ts +5 -0
- package/server/src/lib/queries/get-role-holders-history.query.definition.js +38 -0
- package/server/src/lib/queries/get-role-holders-history.query.definition.js.map +1 -0
- package/server/src/lib/queries/get-role-holders-history.query.js +25 -0
- package/server/src/lib/queries/get-role-holders-history.query.js.map +1 -0
- package/server/src/lib/queries/get-user-effective-permissions-history.query.d.ts +4 -0
- package/server/src/lib/queries/get-user-effective-permissions-history.query.definition.d.ts +5 -0
- package/server/src/lib/queries/get-user-effective-permissions-history.query.definition.js +35 -0
- package/server/src/lib/queries/get-user-effective-permissions-history.query.definition.js.map +1 -0
- package/server/src/lib/queries/get-user-effective-permissions-history.query.js +26 -0
- package/server/src/lib/queries/get-user-effective-permissions-history.query.js.map +1 -0
- package/server/src/lib/queries/get-user-effective-roles-history.query.d.ts +4 -0
- package/server/src/lib/queries/get-user-effective-roles-history.query.definition.d.ts +5 -0
- package/server/src/lib/queries/get-user-effective-roles-history.query.definition.js +36 -0
- package/server/src/lib/queries/get-user-effective-roles-history.query.definition.js.map +1 -0
- package/server/src/lib/queries/get-user-effective-roles-history.query.js +25 -0
- package/server/src/lib/queries/get-user-effective-roles-history.query.js.map +1 -0
- package/server/src/lib/queries/get-user-password-change-history.query.d.ts +4 -0
- package/server/src/lib/queries/get-user-password-change-history.query.definition.d.ts +5 -0
- package/server/src/lib/queries/get-user-password-change-history.query.definition.js +33 -0
- package/server/src/lib/queries/get-user-password-change-history.query.definition.js.map +1 -0
- package/server/src/lib/queries/get-user-password-change-history.query.js +23 -0
- package/server/src/lib/queries/get-user-password-change-history.query.js.map +1 -0
- package/server/src/lib/queries/security-query.helpers.d.ts +47 -0
- package/server/src/lib/queries/security-query.helpers.js +184 -0
- package/server/src/lib/queries/security-query.helpers.js.map +1 -0
- package/server/src/lib/reports/password-change-history.service.d.ts +14 -0
- package/server/src/lib/reports/password-change-history.service.js +74 -0
- package/server/src/lib/reports/password-change-history.service.js.map +1 -0
- package/server/src/lib/reports/permission-display-catalog.d.ts +7 -0
- package/server/src/lib/reports/permission-display-catalog.js +101 -0
- package/server/src/lib/reports/permission-display-catalog.js.map +1 -0
- package/server/src/lib/reports/security-report-definition.provider.d.ts +5 -0
- package/server/src/lib/reports/security-report-definition.provider.js +23 -0
- package/server/src/lib/reports/security-report-definition.provider.js.map +1 -0
- package/server/src/lib/reports/security-report-parameter-groups.d.ts +343 -0
- package/server/src/lib/reports/security-report-parameter-groups.js +167 -0
- package/server/src/lib/reports/security-report-parameter-groups.js.map +1 -0
- package/server/src/lib/reports/security-report-scope.constants.d.ts +5 -0
- package/server/src/lib/reports/security-report-scope.constants.js +11 -0
- package/server/src/lib/reports/security-report-scope.constants.js.map +1 -0
- package/server/src/lib/reports/security-report.definitions.d.ts +12 -0
- package/server/src/lib/reports/security-report.definitions.js +386 -0
- package/server/src/lib/reports/security-report.definitions.js.map +1 -0
- package/server/src/lib/security-management-entity-storage.bootstrap.d.ts +26 -0
- package/server/src/lib/security-management-entity-storage.bootstrap.js +56 -0
- package/server/src/lib/security-management-entity-storage.bootstrap.js.map +1 -0
- package/server/src/lib/security-management-server.module.d.ts +7 -0
- package/server/src/lib/security-management-server.module.js +190 -0
- package/server/src/lib/security-management-server.module.js.map +1 -0
- package/server/src/lib/two-factor/admin-reset-two-factor.command.d.ts +8 -0
- package/server/src/lib/two-factor/admin-reset-two-factor.command.js +35 -0
- package/server/src/lib/two-factor/admin-reset-two-factor.command.js.map +1 -0
- package/server/src/lib/two-factor/confirm-two-factor-enrollment.command.d.ts +8 -0
- package/server/src/lib/two-factor/confirm-two-factor-enrollment.command.js +34 -0
- package/server/src/lib/two-factor/confirm-two-factor-enrollment.command.js.map +1 -0
- package/server/src/lib/two-factor/disable-two-factor.command.d.ts +8 -0
- package/server/src/lib/two-factor/disable-two-factor.command.js +31 -0
- package/server/src/lib/two-factor/disable-two-factor.command.js.map +1 -0
- package/server/src/lib/two-factor/generate-two-factor-recovery-codes.command.d.ts +8 -0
- package/server/src/lib/two-factor/generate-two-factor-recovery-codes.command.js +34 -0
- package/server/src/lib/two-factor/generate-two-factor-recovery-codes.command.js.map +1 -0
- package/server/src/lib/two-factor/get-two-factor-status.command.d.ts +8 -0
- package/server/src/lib/two-factor/get-two-factor-status.command.js +31 -0
- package/server/src/lib/two-factor/get-two-factor-status.command.js.map +1 -0
- package/server/src/lib/two-factor/recovery-codes.util.d.ts +11 -0
- package/server/src/lib/two-factor/recovery-codes.util.js +73 -0
- package/server/src/lib/two-factor/recovery-codes.util.js.map +1 -0
- package/server/src/lib/two-factor/start-two-factor-enrollment.command.d.ts +8 -0
- package/server/src/lib/two-factor/start-two-factor-enrollment.command.js +31 -0
- package/server/src/lib/two-factor/start-two-factor-enrollment.command.js.map +1 -0
- package/server/src/lib/two-factor/totp.util.d.ts +32 -0
- package/server/src/lib/two-factor/totp.util.js +128 -0
- package/server/src/lib/two-factor/totp.util.js.map +1 -0
- package/server/src/lib/two-factor/two-factor-encryption.util.d.ts +25 -0
- package/server/src/lib/two-factor/two-factor-encryption.util.js +123 -0
- package/server/src/lib/two-factor/two-factor-encryption.util.js.map +1 -0
- package/server/src/lib/two-factor/two-factor-gate.bootstrap.d.ts +9 -0
- package/server/src/lib/two-factor/two-factor-gate.bootstrap.js +65 -0
- package/server/src/lib/two-factor/two-factor-gate.bootstrap.js.map +1 -0
- package/server/src/lib/two-factor/two-factor-redis.store.d.ts +63 -0
- package/server/src/lib/two-factor/two-factor-redis.store.js +321 -0
- package/server/src/lib/two-factor/two-factor-redis.store.js.map +1 -0
- package/server/src/lib/two-factor/user-two-factor.service.d.ts +62 -0
- package/server/src/lib/two-factor/user-two-factor.service.js +573 -0
- package/server/src/lib/two-factor/user-two-factor.service.js.map +1 -0
- package/server/src/lib/two-factor/verify-two-factor-login.command.d.ts +18 -0
- package/server/src/lib/two-factor/verify-two-factor-login.command.js +54 -0
- package/server/src/lib/two-factor/verify-two-factor-login.command.js.map +1 -0
- package/server/src/lib/user-invitation/accept-invitation.command.d.ts +8 -0
- package/server/src/lib/user-invitation/accept-invitation.command.js +41 -0
- package/server/src/lib/user-invitation/accept-invitation.command.js.map +1 -0
- package/server/src/lib/user-invitation/accept-my-invitation.command.d.ts +8 -0
- package/server/src/lib/user-invitation/accept-my-invitation.command.js +40 -0
- package/server/src/lib/user-invitation/accept-my-invitation.command.js.map +1 -0
- package/server/src/lib/user-invitation/approve-join-request.command.d.ts +8 -0
- package/server/src/lib/user-invitation/approve-join-request.command.js +41 -0
- package/server/src/lib/user-invitation/approve-join-request.command.js.map +1 -0
- package/server/src/lib/user-invitation/cancel-invitation.command.d.ts +8 -0
- package/server/src/lib/user-invitation/cancel-invitation.command.js +37 -0
- package/server/src/lib/user-invitation/cancel-invitation.command.js.map +1 -0
- package/server/src/lib/user-invitation/cancel-my-join-request.command.d.ts +8 -0
- package/server/src/lib/user-invitation/cancel-my-join-request.command.js +40 -0
- package/server/src/lib/user-invitation/cancel-my-join-request.command.js.map +1 -0
- package/server/src/lib/user-invitation/create-user-in-tenant.command.d.ts +8 -0
- package/server/src/lib/user-invitation/create-user-in-tenant.command.js +37 -0
- package/server/src/lib/user-invitation/create-user-in-tenant.command.js.map +1 -0
- package/server/src/lib/user-invitation/decline-invitation.command.d.ts +8 -0
- package/server/src/lib/user-invitation/decline-invitation.command.js +37 -0
- package/server/src/lib/user-invitation/decline-invitation.command.js.map +1 -0
- package/server/src/lib/user-invitation/invite-user.command.d.ts +8 -0
- package/server/src/lib/user-invitation/invite-user.command.js +37 -0
- package/server/src/lib/user-invitation/invite-user.command.js.map +1 -0
- package/server/src/lib/user-invitation/list-invite-assignable-roles.command.d.ts +8 -0
- package/server/src/lib/user-invitation/list-invite-assignable-roles.command.js +41 -0
- package/server/src/lib/user-invitation/list-invite-assignable-roles.command.js.map +1 -0
- package/server/src/lib/user-invitation/list-my-join-requests.command.d.ts +8 -0
- package/server/src/lib/user-invitation/list-my-join-requests.command.js +40 -0
- package/server/src/lib/user-invitation/list-my-join-requests.command.js.map +1 -0
- package/server/src/lib/user-invitation/list-my-pending-invitations.command.d.ts +8 -0
- package/server/src/lib/user-invitation/list-my-pending-invitations.command.js +37 -0
- package/server/src/lib/user-invitation/list-my-pending-invitations.command.js.map +1 -0
- package/server/src/lib/user-invitation/list-tenant-entitled-applications.command.d.ts +8 -0
- package/server/src/lib/user-invitation/list-tenant-entitled-applications.command.js +41 -0
- package/server/src/lib/user-invitation/list-tenant-entitled-applications.command.js.map +1 -0
- package/server/src/lib/user-invitation/lookup-tenant-by-unique-name.command.d.ts +8 -0
- package/server/src/lib/user-invitation/lookup-tenant-by-unique-name.command.js +40 -0
- package/server/src/lib/user-invitation/lookup-tenant-by-unique-name.command.js.map +1 -0
- package/server/src/lib/user-invitation/reject-join-request.command.d.ts +8 -0
- package/server/src/lib/user-invitation/reject-join-request.command.js +41 -0
- package/server/src/lib/user-invitation/reject-join-request.command.js.map +1 -0
- package/server/src/lib/user-invitation/request-tenant-membership.command.d.ts +8 -0
- package/server/src/lib/user-invitation/request-tenant-membership.command.js +40 -0
- package/server/src/lib/user-invitation/request-tenant-membership.command.js.map +1 -0
- package/server/src/lib/user-invitation/resend-invitation.command.d.ts +12 -0
- package/server/src/lib/user-invitation/resend-invitation.command.js +37 -0
- package/server/src/lib/user-invitation/resend-invitation.command.js.map +1 -0
- package/server/src/lib/user-invitation/user-invitation-application-access.util.d.ts +25 -0
- package/server/src/lib/user-invitation/user-invitation-application-access.util.js +121 -0
- package/server/src/lib/user-invitation/user-invitation-application-access.util.js.map +1 -0
- package/server/src/lib/user-invitation/user-invitation.service.d.ts +162 -0
- package/server/src/lib/user-invitation/user-invitation.service.js +1658 -0
- package/server/src/lib/user-invitation/user-invitation.service.js.map +1 -0
- package/contracts/src/datasource.keys.d.ts +0 -4
- package/contracts/src/datasource.keys.js +0 -10
- package/contracts/src/datasource.keys.js.map +0 -1
- package/src/index.d.ts +0 -5
- package/src/index.js +0 -13
- package/src/index.js.map +0 -1
- package/src/lib/middlewares/user-effective-state.middleware.js.map +0 -1
- package/src/lib/password-reset/complete-password-reset.command.js.map +0 -1
- package/src/lib/password-reset/send-password-reset-email.command.js.map +0 -1
- package/src/lib/password-reset/user-password-reset.service.js.map +0 -1
- package/src/lib/security-management-server.module.d.ts +0 -2
- package/src/lib/security-management-server.module.js +0 -48
- package/src/lib/security-management-server.module.js.map +0 -1
- package/src/lib/seed/index.d.ts +0 -22
- package/src/lib/seed/index.js +0 -24
- package/src/lib/seed/index.js.map +0 -1
- package/src/lib/seed/policy/index.d.ts +0 -2
- package/src/lib/seed/policy/index.js +0 -6
- package/src/lib/seed/policy/index.js.map +0 -1
- package/src/lib/seed/policy/policy.mock.d.ts +0 -7
- package/src/lib/seed/policy/policy.mock.js +0 -40
- package/src/lib/seed/policy/policy.mock.js.map +0 -1
- package/src/lib/seed/policy/policy.seed.d.ts +0 -5
- package/src/lib/seed/policy/policy.seed.js +0 -31
- package/src/lib/seed/policy/policy.seed.js.map +0 -1
- package/src/lib/seed/role/index.d.ts +0 -4
- package/src/lib/seed/role/index.js +0 -8
- package/src/lib/seed/role/index.js.map +0 -1
- package/src/lib/seed/role/role-ids.constants.d.ts +0 -14
- package/src/lib/seed/role/role-ids.constants.js +0 -20
- package/src/lib/seed/role/role-ids.constants.js.map +0 -1
- package/src/lib/seed/role/role-permission-keys.constants.d.ts +0 -578
- package/src/lib/seed/role/role-permission-keys.constants.js +0 -620
- package/src/lib/seed/role/role-permission-keys.constants.js.map +0 -1
- package/src/lib/seed/role/role.mock.d.ts +0 -4
- package/src/lib/seed/role/role.mock.js +0 -321
- package/src/lib/seed/role/role.mock.js.map +0 -1
- package/src/lib/seed/role/role.seed.d.ts +0 -5
- package/src/lib/seed/role/role.seed.js +0 -31
- package/src/lib/seed/role/role.seed.js.map +0 -1
- package/src/lib/seed/security-management-seed.provider.d.ts +0 -4
- package/src/lib/seed/security-management-seed.provider.js +0 -18
- package/src/lib/seed/security-management-seed.provider.js.map +0 -1
- package/src/lib/seed/security-management-seed.tokens.d.ts +0 -1
- package/src/lib/seed/security-management-seed.tokens.js +0 -7
- package/src/lib/seed/security-management-seed.tokens.js.map +0 -1
- package/src/lib/seed/security-management.seed-catalog.d.ts +0 -2
- package/src/lib/seed/security-management.seed-catalog.js +0 -17
- package/src/lib/seed/security-management.seed-catalog.js.map +0 -1
- package/src/lib/seed/tenant/index.d.ts +0 -1
- package/src/lib/seed/tenant/tenant-ids.constants.d.ts +0 -11
- package/src/lib/seed/tenant/tenant-ids.constants.js +0 -17
- package/src/lib/seed/tenant/tenant-ids.constants.js.map +0 -1
- package/src/lib/seed/user/index.d.ts +0 -4
- package/src/lib/seed/user/index.js +0 -13
- package/src/lib/seed/user/index.js.map +0 -1
- package/src/lib/seed/user/resolve-user-effective-state.util.d.ts +0 -4
- package/src/lib/seed/user/resolve-user-effective-state.util.js +0 -10
- package/src/lib/seed/user/resolve-user-effective-state.util.js.map +0 -1
- package/src/lib/seed/user/user.mock.d.ts +0 -21
- package/src/lib/seed/user/user.mock.js +0 -338
- package/src/lib/seed/user/user.mock.js.map +0 -1
- package/src/lib/seed/user/user.seed.d.ts +0 -5
- package/src/lib/seed/user/user.seed.js +0 -31
- package/src/lib/seed/user/user.seed.js.map +0 -1
- /package/{src → server/src}/lib/middlewares/user-effective-state.middleware.d.ts +0 -0
|
@@ -0,0 +1,1658 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var UserInvitationService_1;
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.UserInvitationService = void 0;
|
|
5
|
+
const tslib_1 = require("tslib");
|
|
6
|
+
const common_1 = require("@nestjs/common");
|
|
7
|
+
const crypto_1 = require("crypto");
|
|
8
|
+
const lodash_1 = require("lodash");
|
|
9
|
+
const core_1 = require("@acorex-platform/framework-server/core");
|
|
10
|
+
const providers_1 = require("@acorex-platform/framework-server/domain/providers");
|
|
11
|
+
const abstract_1 = require("@acorex-platform/framework-server/jql/abstract");
|
|
12
|
+
const security_1 = require("@acorex-platform/framework-server/security");
|
|
13
|
+
const core_2 = require("@acorex-platform/framework-shared/core");
|
|
14
|
+
const module_communication_management_contracts_1 = require("@acorex-platform/module-communication-management-client/contracts");
|
|
15
|
+
const module_content_management_contracts_1 = require("@acorex-platform/module-content-management-client/contracts");
|
|
16
|
+
const module_security_management_contracts_1 = require("@acorex-platform/module-security-management-client/contracts");
|
|
17
|
+
const module_auth_contracts_1 = require("@acorex-platform/module-auth-client/contracts");
|
|
18
|
+
const user_invitation_application_access_util_1 = require("./user-invitation-application-access.util");
|
|
19
|
+
//#region ---- Constants ----
|
|
20
|
+
const USER_ENTITY = 'SecurityManagement.User';
|
|
21
|
+
const ROLE_ENTITY = 'SecurityManagement.Role';
|
|
22
|
+
const USER_ROLE_ENTITY = 'SecurityManagement.UserRole';
|
|
23
|
+
const TENANT_ENTITY = 'TenantManagement.Tenant';
|
|
24
|
+
const TENANT_USER_ENTITY = 'TenantManagement.TenantUser';
|
|
25
|
+
const APP_ACCESS_ENTITY = 'TenantManagement.TenantUserAppAccess';
|
|
26
|
+
const TEMPLATE_ID = module_content_management_contracts_1.AXM_TEXT_TEMPLATE_IDS.userInvitation;
|
|
27
|
+
const EMAIL_CHANNEL = 'email-smtp';
|
|
28
|
+
const EXPIRY_MINUTES = 7 * 24 * 60;
|
|
29
|
+
const JOIN_REQUEST_EXPIRY_MINUTES = 30 * 24 * 60;
|
|
30
|
+
const ACTIVE_ACCOUNT_STATE = 'active';
|
|
31
|
+
const PENDING_ACCOUNT_STATE = 'pending';
|
|
32
|
+
function hashToken(token) {
|
|
33
|
+
return (0, crypto_1.createHash)('sha256').update(token).digest('hex');
|
|
34
|
+
}
|
|
35
|
+
function tokensMatch(left, right) {
|
|
36
|
+
const a = Buffer.from(left);
|
|
37
|
+
const b = Buffer.from(right);
|
|
38
|
+
if (a.length !== b.length) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
return (0, crypto_1.timingSafeEqual)(a, b);
|
|
42
|
+
}
|
|
43
|
+
function readGrantId(row) {
|
|
44
|
+
const id = row === null || row === void 0 ? void 0 : row['id'];
|
|
45
|
+
return typeof id === 'string' && id.length > 0 ? id : undefined;
|
|
46
|
+
}
|
|
47
|
+
function resolveWebOrigin(explicit) {
|
|
48
|
+
var _a, _b, _c;
|
|
49
|
+
const fromInput = explicit === null || explicit === void 0 ? void 0 : explicit.trim();
|
|
50
|
+
if (fromInput) {
|
|
51
|
+
return fromInput.replace(/\/$/, '');
|
|
52
|
+
}
|
|
53
|
+
const fromEnv = ((_a = process.env['ACOREX_WEB_ORIGIN']) === null || _a === void 0 ? void 0 : _a.trim()) ||
|
|
54
|
+
((_c = (_b = process.env['CORS_ORIGINS']) === null || _b === void 0 ? void 0 : _b.split(',')[0]) === null || _c === void 0 ? void 0 : _c.trim()) ||
|
|
55
|
+
'http://localhost:4800';
|
|
56
|
+
return fromEnv.replace(/\/$/, '');
|
|
57
|
+
}
|
|
58
|
+
function normalizeEmail(value) {
|
|
59
|
+
return value.trim().toLowerCase();
|
|
60
|
+
}
|
|
61
|
+
function buildInvitation(invitedByUserId, applicationIds, roleIds, applicationRoleAssignments) {
|
|
62
|
+
const rawToken = (0, crypto_1.randomBytes)(32).toString('hex');
|
|
63
|
+
const now = new Date();
|
|
64
|
+
return {
|
|
65
|
+
rawToken,
|
|
66
|
+
invitation: Object.assign(Object.assign(Object.assign({ tokenHash: hashToken(rawToken), expiresAt: new Date(now.getTime() + EXPIRY_MINUTES * 60000).toISOString(), sentAt: now.toISOString(), invitedByUserId,
|
|
67
|
+
applicationIds }, (roleIds.length > 0 ? { roleIds } : {})), ((applicationRoleAssignments === null || applicationRoleAssignments === void 0 ? void 0 : applicationRoleAssignments.length)
|
|
68
|
+
? { applicationRoleAssignments }
|
|
69
|
+
: {})), { source: module_security_management_contracts_1.AXM_MEMBERSHIP_INVITATION_SOURCE.Invite }),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/** Keep invite audit fields; drop the one-time token secret. */
|
|
73
|
+
function invitationWithoutToken(invitation, patch) {
|
|
74
|
+
var _a;
|
|
75
|
+
const { tokenHash: _tokenHash } = invitation, rest = tslib_1.__rest(invitation, ["tokenHash"]);
|
|
76
|
+
return Object.assign(Object.assign(Object.assign({}, rest), { source: (_a = rest.source) !== null && _a !== void 0 ? _a : module_security_management_contracts_1.AXM_MEMBERSHIP_INVITATION_SOURCE.Invite }), patch);
|
|
77
|
+
}
|
|
78
|
+
//#endregion
|
|
79
|
+
//#region ---- User invitation service ----
|
|
80
|
+
let UserInvitationService = UserInvitationService_1 = class UserInvitationService {
|
|
81
|
+
constructor(entityStorage, commandExecutor) {
|
|
82
|
+
this.entityStorage = entityStorage;
|
|
83
|
+
this.commandExecutor = commandExecutor;
|
|
84
|
+
this.logger = new common_1.Logger(UserInvitationService_1.name);
|
|
85
|
+
}
|
|
86
|
+
//#region ---- Admin flows ----
|
|
87
|
+
inviteUser(input) {
|
|
88
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
89
|
+
var _a, _b, _c, _d, _e;
|
|
90
|
+
const session = this.requireSession();
|
|
91
|
+
const tenantId = (_a = session.tenantId) === null || _a === void 0 ? void 0 : _a.trim();
|
|
92
|
+
if (!tenantId) {
|
|
93
|
+
return { success: false, message: 'Tenant context is required.' };
|
|
94
|
+
}
|
|
95
|
+
const email = normalizeEmail((_b = input.email) !== null && _b !== void 0 ? _b : '');
|
|
96
|
+
if (!email.includes('@')) {
|
|
97
|
+
return { success: false, message: 'A valid email address is required.' };
|
|
98
|
+
}
|
|
99
|
+
const tenantMembershipInvite = input.tenantMembershipInvite === true;
|
|
100
|
+
const requestedAssignments = tenantMembershipInvite
|
|
101
|
+
? (0, module_security_management_contracts_1.normalizeInviteApplicationRoleAssignments)(input.applicationRoleAssignments)
|
|
102
|
+
: [];
|
|
103
|
+
const applicationIds = yield this.resolveAuthorizedApplicationIds([
|
|
104
|
+
...((_c = input.applicationIds) !== null && _c !== void 0 ? _c : []),
|
|
105
|
+
...requestedAssignments.map((row) => row.applicationId),
|
|
106
|
+
], session, tenantId, tenantMembershipInvite);
|
|
107
|
+
if (applicationIds.length === 0) {
|
|
108
|
+
return {
|
|
109
|
+
success: false,
|
|
110
|
+
message: 'No valid application access can be granted for this invitation.',
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
const authorizedAssignments = [];
|
|
114
|
+
for (const row of requestedAssignments) {
|
|
115
|
+
if (!applicationIds.includes(row.applicationId)) {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
const rowRoleIds = yield this.resolveAuthorizedTenantUserInviteRoleIds(row.roleIds, tenantId, row.applicationId);
|
|
119
|
+
authorizedAssignments.push({
|
|
120
|
+
applicationId: row.applicationId,
|
|
121
|
+
roleIds: rowRoleIds,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
const roleIds = authorizedAssignments.length > 0
|
|
125
|
+
? [...new Set(authorizedAssignments.flatMap((row) => row.roleIds))]
|
|
126
|
+
: yield this.resolveAuthorizedRoleIds(input.roleIds, session, tenantId, tenantMembershipInvite ? 'tenant' : 'application');
|
|
127
|
+
const displayName = ((_d = input.displayName) !== null && _d !== void 0 ? _d : '').trim() || email.split('@')[0];
|
|
128
|
+
let user = yield this.findUserByEmail(email);
|
|
129
|
+
let userAlreadyExists = false;
|
|
130
|
+
if (user === null || user === void 0 ? void 0 : user.id) {
|
|
131
|
+
userAlreadyExists = true;
|
|
132
|
+
const existingMembership = yield this.findTenantUser(tenantId, user.id);
|
|
133
|
+
if ((existingMembership === null || existingMembership === void 0 ? void 0 : existingMembership.statusId) === core_2.AXPSystemStatusType.Active) {
|
|
134
|
+
return {
|
|
135
|
+
success: false,
|
|
136
|
+
message: 'This user is already an active member of the tenant.',
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
if (!input.confirmExisting) {
|
|
140
|
+
return {
|
|
141
|
+
success: true,
|
|
142
|
+
data: {
|
|
143
|
+
userId: user.id,
|
|
144
|
+
tenantUserId: (_e = existingMembership === null || existingMembership === void 0 ? void 0 : existingMembership.id) !== null && _e !== void 0 ? _e : '',
|
|
145
|
+
userAlreadyExists: true,
|
|
146
|
+
sent: false,
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
const userId = (0, crypto_1.randomUUID)();
|
|
153
|
+
yield this.entityStorage.insertOneAsEngine(USER_ENTITY, {
|
|
154
|
+
id: userId,
|
|
155
|
+
username: email,
|
|
156
|
+
email,
|
|
157
|
+
displayName,
|
|
158
|
+
accountState: PENDING_ACCOUNT_STATE,
|
|
159
|
+
homeTenantId: tenantId,
|
|
160
|
+
createdVia: 'invite',
|
|
161
|
+
createdByUserId: session.userId,
|
|
162
|
+
accessState: module_security_management_contracts_1.AXMUserAccessState.Allowed,
|
|
163
|
+
password: yield (0, core_2.hashPassword)((0, crypto_1.randomBytes)(32).toString('hex')),
|
|
164
|
+
});
|
|
165
|
+
user = (yield this.entityStorage.getOneAsEngine(USER_ENTITY, userId));
|
|
166
|
+
if (!(user === null || user === void 0 ? void 0 : user.id)) {
|
|
167
|
+
return { success: false, message: 'Failed to create user.' };
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
const { rawToken, invitation } = buildInvitation(session.userId, applicationIds, roleIds, authorizedAssignments);
|
|
171
|
+
const tenantUserId = yield this.upsertPendingTenantUser(tenantId, user.id, invitation);
|
|
172
|
+
yield this.upsertPendingAppAccess(tenantId, tenantUserId, applicationIds);
|
|
173
|
+
if (authorizedAssignments.length > 0) {
|
|
174
|
+
for (const row of authorizedAssignments) {
|
|
175
|
+
yield this.upsertInviteUserRoles({
|
|
176
|
+
userId: user.id,
|
|
177
|
+
tenantId,
|
|
178
|
+
applicationId: row.applicationId,
|
|
179
|
+
roleIds: row.roleIds,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
yield this.upsertInviteUserRoles({
|
|
185
|
+
userId: user.id,
|
|
186
|
+
tenantId,
|
|
187
|
+
applicationId: tenantMembershipInvite ? undefined : session.applicationId,
|
|
188
|
+
roleIds,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
try {
|
|
192
|
+
const sent = yield this.deliverInvitationEmail({
|
|
193
|
+
user,
|
|
194
|
+
email,
|
|
195
|
+
rawToken,
|
|
196
|
+
invitation,
|
|
197
|
+
webOrigin: input.webOrigin,
|
|
198
|
+
});
|
|
199
|
+
yield this.maybeDeliverInvitationInApp({
|
|
200
|
+
user,
|
|
201
|
+
tenantId,
|
|
202
|
+
tenantUserId,
|
|
203
|
+
userAlreadyExists,
|
|
204
|
+
});
|
|
205
|
+
return {
|
|
206
|
+
success: true,
|
|
207
|
+
data: {
|
|
208
|
+
userId: user.id,
|
|
209
|
+
tenantUserId,
|
|
210
|
+
userAlreadyExists,
|
|
211
|
+
sent,
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
return {
|
|
217
|
+
success: false,
|
|
218
|
+
message: error instanceof Error ? error.message : 'Failed to send invitation email.',
|
|
219
|
+
data: {
|
|
220
|
+
userId: user.id,
|
|
221
|
+
tenantUserId,
|
|
222
|
+
userAlreadyExists,
|
|
223
|
+
sent: false,
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
resendInvitation(input) {
|
|
230
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
231
|
+
var _a, _b, _c, _d, _e;
|
|
232
|
+
const session = this.requireSession();
|
|
233
|
+
const tenantId = (_a = session.tenantId) === null || _a === void 0 ? void 0 : _a.trim();
|
|
234
|
+
if (!tenantId) {
|
|
235
|
+
return { success: false, message: 'Tenant context is required.' };
|
|
236
|
+
}
|
|
237
|
+
const membership = yield this.resolveMembership(tenantId, input.tenantUserId, input.userId);
|
|
238
|
+
if (!(membership === null || membership === void 0 ? void 0 : membership.id) || !membership.userId) {
|
|
239
|
+
return { success: false, message: 'Pending invitation not found.' };
|
|
240
|
+
}
|
|
241
|
+
const user = (yield this.entityStorage.getOneAsEngine(USER_ENTITY, membership.userId));
|
|
242
|
+
const email = (_b = user === null || user === void 0 ? void 0 : user.email) === null || _b === void 0 ? void 0 : _b.trim();
|
|
243
|
+
if (!(user === null || user === void 0 ? void 0 : user.id) || !email) {
|
|
244
|
+
return { success: false, message: 'User does not have an email address.' };
|
|
245
|
+
}
|
|
246
|
+
const applicationIds = ((_d = (_c = membership.invitation) === null || _c === void 0 ? void 0 : _c.applicationIds) === null || _d === void 0 ? void 0 : _d.length)
|
|
247
|
+
? yield this.revalidateApplicationIds(tenantId, membership.invitation.applicationIds)
|
|
248
|
+
: yield this.resolveAuthorizedApplicationIds(undefined, session, tenantId, false);
|
|
249
|
+
if (applicationIds.length === 0) {
|
|
250
|
+
return {
|
|
251
|
+
success: false,
|
|
252
|
+
message: 'No valid application access remains for this invitation.',
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
const existingRoleIds = Array.isArray((_e = membership.invitation) === null || _e === void 0 ? void 0 : _e.roleIds)
|
|
256
|
+
? membership.invitation.roleIds.map((id) => String(id).trim()).filter(Boolean)
|
|
257
|
+
: [];
|
|
258
|
+
const { rawToken, invitation } = buildInvitation(session.userId, applicationIds, existingRoleIds);
|
|
259
|
+
yield this.entityStorage.updateOneAsEngine(TENANT_USER_ENTITY, membership.id, {
|
|
260
|
+
statusId: core_2.AXPSystemStatusType.Pending,
|
|
261
|
+
invitation,
|
|
262
|
+
endDate: null,
|
|
263
|
+
});
|
|
264
|
+
yield this.upsertPendingAppAccess(tenantId, membership.id, applicationIds);
|
|
265
|
+
try {
|
|
266
|
+
const sent = yield this.deliverInvitationEmail({
|
|
267
|
+
user,
|
|
268
|
+
email,
|
|
269
|
+
rawToken,
|
|
270
|
+
invitation,
|
|
271
|
+
webOrigin: input.webOrigin,
|
|
272
|
+
});
|
|
273
|
+
yield this.maybeDeliverInvitationInApp({
|
|
274
|
+
user,
|
|
275
|
+
tenantId,
|
|
276
|
+
tenantUserId: membership.id,
|
|
277
|
+
userAlreadyExists: true,
|
|
278
|
+
});
|
|
279
|
+
return { success: true, sent };
|
|
280
|
+
}
|
|
281
|
+
catch (error) {
|
|
282
|
+
return {
|
|
283
|
+
success: false,
|
|
284
|
+
message: error instanceof Error ? error.message : 'Failed to send invitation email.',
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
cancelInvitation(input) {
|
|
290
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
291
|
+
var _a, _b;
|
|
292
|
+
const session = this.requireSession();
|
|
293
|
+
const tenantId = (_a = session.tenantId) === null || _a === void 0 ? void 0 : _a.trim();
|
|
294
|
+
if (!tenantId) {
|
|
295
|
+
return { success: false, message: 'Tenant context is required.' };
|
|
296
|
+
}
|
|
297
|
+
const membership = yield this.resolveMembership(tenantId, input.tenantUserId, input.userId);
|
|
298
|
+
if (!(membership === null || membership === void 0 ? void 0 : membership.id) || !membership.userId) {
|
|
299
|
+
return { success: false, message: 'Invitation not found.' };
|
|
300
|
+
}
|
|
301
|
+
// Only pending admin-sent invites can be cancelled (not accepted / join-request).
|
|
302
|
+
if (membership.statusId !== core_2.AXPSystemStatusType.Pending ||
|
|
303
|
+
((_b = membership.invitation) === null || _b === void 0 ? void 0 : _b.source) !== module_security_management_contracts_1.AXM_MEMBERSHIP_INVITATION_SOURCE.Invite) {
|
|
304
|
+
return {
|
|
305
|
+
success: false,
|
|
306
|
+
message: 'Only a pending invitation can be cancelled.',
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
yield this.resolveMembershipStatus(membership, core_2.AXPSystemStatusType.Cancelled);
|
|
310
|
+
return { success: true };
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
approveJoinRequest(input) {
|
|
314
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
315
|
+
var _a, _b;
|
|
316
|
+
const session = this.requireSession();
|
|
317
|
+
const tenantId = (_a = session.tenantId) === null || _a === void 0 ? void 0 : _a.trim();
|
|
318
|
+
if (!tenantId) {
|
|
319
|
+
return {
|
|
320
|
+
success: false,
|
|
321
|
+
code: module_security_management_contracts_1.AXM_JOIN_REQUEST_ERROR_CODES.InvalidInput,
|
|
322
|
+
message: 'Tenant context is required.',
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
const membership = yield this.resolveMembership(tenantId, input.tenantUserId, input.userId);
|
|
326
|
+
if (!this.isPendingJoinRequest(membership) || !(membership === null || membership === void 0 ? void 0 : membership.id) || !membership.userId) {
|
|
327
|
+
return {
|
|
328
|
+
success: false,
|
|
329
|
+
code: module_security_management_contracts_1.AXM_JOIN_REQUEST_ERROR_CODES.NotPending,
|
|
330
|
+
message: 'Join request is not pending.',
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
const tenantMembershipApprove = input.tenantMembershipApprove === true;
|
|
334
|
+
const requestedAssignments = tenantMembershipApprove
|
|
335
|
+
? (0, module_security_management_contracts_1.normalizeInviteApplicationRoleAssignments)(input.applicationRoleAssignments)
|
|
336
|
+
: [];
|
|
337
|
+
const applicationIds = yield this.resolveAuthorizedApplicationIds([
|
|
338
|
+
...((_b = input.applicationIds) !== null && _b !== void 0 ? _b : []),
|
|
339
|
+
...requestedAssignments.map((row) => row.applicationId),
|
|
340
|
+
], session, tenantId, tenantMembershipApprove);
|
|
341
|
+
if (applicationIds.length === 0) {
|
|
342
|
+
return {
|
|
343
|
+
success: false,
|
|
344
|
+
code: module_security_management_contracts_1.AXM_JOIN_REQUEST_ERROR_CODES.InvalidInput,
|
|
345
|
+
message: 'No valid application access can be granted for this approval.',
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
const authorizedAssignments = [];
|
|
349
|
+
for (const row of requestedAssignments) {
|
|
350
|
+
if (!applicationIds.includes(row.applicationId)) {
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
const rowRoleIds = yield this.resolveAuthorizedTenantUserInviteRoleIds(row.roleIds, tenantId, row.applicationId);
|
|
354
|
+
authorizedAssignments.push({
|
|
355
|
+
applicationId: row.applicationId,
|
|
356
|
+
roleIds: rowRoleIds,
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
const roleIds = authorizedAssignments.length > 0
|
|
360
|
+
? [...new Set(authorizedAssignments.flatMap((row) => row.roleIds))]
|
|
361
|
+
: yield this.resolveAuthorizedRoleIds(input.roleIds, session, tenantId, tenantMembershipApprove ? 'tenant' : 'application');
|
|
362
|
+
if (authorizedAssignments.length > 0 && authorizedAssignments.some((row) => row.roleIds.length === 0)) {
|
|
363
|
+
return {
|
|
364
|
+
success: false,
|
|
365
|
+
code: module_security_management_contracts_1.AXM_JOIN_REQUEST_ERROR_CODES.InvalidInput,
|
|
366
|
+
message: 'Each selected application must include at least one role.',
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
if (authorizedAssignments.length === 0 && roleIds.length === 0) {
|
|
370
|
+
return {
|
|
371
|
+
success: false,
|
|
372
|
+
code: module_security_management_contracts_1.AXM_JOIN_REQUEST_ERROR_CODES.InvalidInput,
|
|
373
|
+
message: 'At least one role is required.',
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
const invitation = Object.assign(Object.assign(Object.assign(Object.assign({}, membership.invitation), { applicationIds }), (roleIds.length > 0 ? { roleIds } : {})), (authorizedAssignments.length > 0 ? { applicationRoleAssignments: authorizedAssignments } : {}));
|
|
377
|
+
yield this.entityStorage.updateOneAsEngine(TENANT_USER_ENTITY, membership.id, {
|
|
378
|
+
invitation,
|
|
379
|
+
});
|
|
380
|
+
membership.invitation = invitation;
|
|
381
|
+
yield this.upsertPendingAppAccess(tenantId, membership.id, applicationIds);
|
|
382
|
+
if (authorizedAssignments.length > 0) {
|
|
383
|
+
for (const row of authorizedAssignments) {
|
|
384
|
+
yield this.upsertInviteUserRoles({
|
|
385
|
+
userId: membership.userId,
|
|
386
|
+
tenantId,
|
|
387
|
+
applicationId: row.applicationId,
|
|
388
|
+
roleIds: row.roleIds,
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
else {
|
|
393
|
+
yield this.upsertInviteUserRoles({
|
|
394
|
+
userId: membership.userId,
|
|
395
|
+
tenantId,
|
|
396
|
+
applicationId: tenantMembershipApprove ? undefined : session.applicationId,
|
|
397
|
+
roleIds,
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
return this.activateMembership(membership);
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
rejectJoinRequest(input) {
|
|
404
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
405
|
+
var _a;
|
|
406
|
+
const session = this.requireSession();
|
|
407
|
+
const tenantId = (_a = session.tenantId) === null || _a === void 0 ? void 0 : _a.trim();
|
|
408
|
+
if (!tenantId) {
|
|
409
|
+
return {
|
|
410
|
+
success: false,
|
|
411
|
+
code: module_security_management_contracts_1.AXM_JOIN_REQUEST_ERROR_CODES.InvalidInput,
|
|
412
|
+
message: 'Tenant context is required.',
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
const membership = yield this.resolveMembership(tenantId, input.tenantUserId, input.userId);
|
|
416
|
+
if (!this.isPendingJoinRequest(membership) || !(membership === null || membership === void 0 ? void 0 : membership.id)) {
|
|
417
|
+
return {
|
|
418
|
+
success: false,
|
|
419
|
+
code: module_security_management_contracts_1.AXM_JOIN_REQUEST_ERROR_CODES.NotPending,
|
|
420
|
+
message: 'Join request is not pending.',
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
yield this.resolveMembershipStatus(membership, core_2.AXPSystemStatusType.Rejected);
|
|
424
|
+
return { success: true };
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
createUserInTenant(input) {
|
|
428
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
429
|
+
var _a, _b, _c, _d, _e, _f;
|
|
430
|
+
const session = this.requireSession();
|
|
431
|
+
const tenantId = (_a = session.tenantId) === null || _a === void 0 ? void 0 : _a.trim();
|
|
432
|
+
if (!tenantId) {
|
|
433
|
+
return { success: false, message: 'Tenant context is required.' };
|
|
434
|
+
}
|
|
435
|
+
const email = input.email ? normalizeEmail(input.email) : undefined;
|
|
436
|
+
// Prefer email as login id when present; username is fallback only.
|
|
437
|
+
const username = ((_b = email !== null && email !== void 0 ? email : input.username) !== null && _b !== void 0 ? _b : '').trim().toLowerCase();
|
|
438
|
+
if (!username) {
|
|
439
|
+
return { success: false, message: 'Username or email is required.' };
|
|
440
|
+
}
|
|
441
|
+
if (email && ((_c = (yield this.findUserByEmail(email))) === null || _c === void 0 ? void 0 : _c.id)) {
|
|
442
|
+
return {
|
|
443
|
+
success: false,
|
|
444
|
+
message: 'A user with this email already exists.',
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
if ((_d = (yield this.findUserByUsername(username))) === null || _d === void 0 ? void 0 : _d.id) {
|
|
448
|
+
return { success: false, message: 'A user with this username already exists.' };
|
|
449
|
+
}
|
|
450
|
+
const passwordPlain = ((_e = input.password) !== null && _e !== void 0 ? _e : '').trim();
|
|
451
|
+
if (passwordPlain.length < 6) {
|
|
452
|
+
return {
|
|
453
|
+
success: false,
|
|
454
|
+
message: 'A password of at least 6 characters is required.',
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
const tenantMembershipCreate = input.tenantMembershipCreate === true;
|
|
458
|
+
const createAssignments = yield this.resolveCreateInTenantRoleAssignments(input, session, tenantId, tenantMembershipCreate);
|
|
459
|
+
if (!createAssignments.ok) {
|
|
460
|
+
return { success: false, message: createAssignments.message };
|
|
461
|
+
}
|
|
462
|
+
const { applicationIds, authorizedAssignments, roleIds } = createAssignments;
|
|
463
|
+
const displayName = ((_f = input.displayName) !== null && _f !== void 0 ? _f : '').trim() || username;
|
|
464
|
+
const userId = (0, crypto_1.randomUUID)();
|
|
465
|
+
yield this.entityStorage.insertOneAsEngine(USER_ENTITY, {
|
|
466
|
+
id: userId,
|
|
467
|
+
username,
|
|
468
|
+
email: email || (username.includes('@') ? username : undefined),
|
|
469
|
+
displayName,
|
|
470
|
+
accountState: ACTIVE_ACCOUNT_STATE,
|
|
471
|
+
homeTenantId: tenantId,
|
|
472
|
+
createdVia: 'create-in-tenant',
|
|
473
|
+
accessState: module_security_management_contracts_1.AXMUserAccessState.Allowed,
|
|
474
|
+
password: yield (0, core_2.hashPassword)(passwordPlain),
|
|
475
|
+
passwordChangedAt: new Date().toISOString(),
|
|
476
|
+
});
|
|
477
|
+
const tenantUserId = (0, crypto_1.randomUUID)();
|
|
478
|
+
yield this.entityStorage.insertOneAsEngine(TENANT_USER_ENTITY, {
|
|
479
|
+
id: tenantUserId,
|
|
480
|
+
tenantId,
|
|
481
|
+
userId,
|
|
482
|
+
statusId: core_2.AXPSystemStatusType.Active,
|
|
483
|
+
startDate: new Date(),
|
|
484
|
+
invitation: null,
|
|
485
|
+
});
|
|
486
|
+
for (const applicationId of applicationIds) {
|
|
487
|
+
yield this.entityStorage.insertOneAsEngine(APP_ACCESS_ENTITY, {
|
|
488
|
+
id: (0, crypto_1.randomUUID)(),
|
|
489
|
+
tenantUserId,
|
|
490
|
+
tenantId,
|
|
491
|
+
applicationId,
|
|
492
|
+
statusId: core_2.AXPSystemStatusType.Active,
|
|
493
|
+
startDate: new Date(),
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
yield this.assignCreateInTenantUserRoles({
|
|
497
|
+
userId,
|
|
498
|
+
tenantId,
|
|
499
|
+
sessionApplicationId: session.applicationId,
|
|
500
|
+
tenantMembershipCreate,
|
|
501
|
+
authorizedAssignments,
|
|
502
|
+
roleIds,
|
|
503
|
+
});
|
|
504
|
+
return { success: true, data: { userId, tenantUserId } };
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Create-in-tenant only: authorize apps + roles (does not call inviteUser).
|
|
509
|
+
*/
|
|
510
|
+
resolveCreateInTenantRoleAssignments(input, session, tenantId, tenantMembershipCreate) {
|
|
511
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
512
|
+
var _a;
|
|
513
|
+
const requestedAssignments = tenantMembershipCreate
|
|
514
|
+
? (0, module_security_management_contracts_1.normalizeCreateApplicationRoleAssignments)(input.applicationRoleAssignments)
|
|
515
|
+
: [];
|
|
516
|
+
const applicationIds = yield this.resolveAuthorizedApplicationIds([
|
|
517
|
+
...((_a = input.applicationIds) !== null && _a !== void 0 ? _a : []),
|
|
518
|
+
...requestedAssignments.map((row) => row.applicationId),
|
|
519
|
+
], session, tenantId, tenantMembershipCreate);
|
|
520
|
+
if (applicationIds.length === 0) {
|
|
521
|
+
return {
|
|
522
|
+
ok: false,
|
|
523
|
+
message: 'No valid application access can be granted for this user.',
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
const authorizedAssignments = [];
|
|
527
|
+
for (const row of requestedAssignments) {
|
|
528
|
+
if (!applicationIds.includes(row.applicationId)) {
|
|
529
|
+
continue;
|
|
530
|
+
}
|
|
531
|
+
const rowRoleIds = yield this.resolveAuthorizedTenantUserInviteRoleIds(row.roleIds, tenantId, row.applicationId);
|
|
532
|
+
authorizedAssignments.push({
|
|
533
|
+
applicationId: row.applicationId,
|
|
534
|
+
roleIds: rowRoleIds,
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
const roleIds = authorizedAssignments.length > 0
|
|
538
|
+
? [...new Set(authorizedAssignments.flatMap((row) => row.roleIds))]
|
|
539
|
+
: yield this.resolveAuthorizedRoleIds(input.roleIds, session, tenantId, tenantMembershipCreate ? 'tenant' : 'application');
|
|
540
|
+
if (tenantMembershipCreate) {
|
|
541
|
+
if (authorizedAssignments.length === 0) {
|
|
542
|
+
return { ok: false, message: 'Select at least one application with roles.' };
|
|
543
|
+
}
|
|
544
|
+
if (authorizedAssignments.some((row) => row.roleIds.length === 0)) {
|
|
545
|
+
return {
|
|
546
|
+
ok: false,
|
|
547
|
+
message: 'Each selected application must include at least one role.',
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
else if (roleIds.length === 0) {
|
|
552
|
+
return { ok: false, message: 'At least one role is required.' };
|
|
553
|
+
}
|
|
554
|
+
return { ok: true, applicationIds, authorizedAssignments, roleIds };
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
/** Create-in-tenant only: write UserRole grants after identity + AppAccess. */
|
|
558
|
+
assignCreateInTenantUserRoles(options) {
|
|
559
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
560
|
+
if (options.authorizedAssignments.length > 0) {
|
|
561
|
+
for (const row of options.authorizedAssignments) {
|
|
562
|
+
yield this.upsertCreateInTenantUserRoles({
|
|
563
|
+
userId: options.userId,
|
|
564
|
+
tenantId: options.tenantId,
|
|
565
|
+
applicationId: row.applicationId,
|
|
566
|
+
roleIds: row.roleIds,
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
yield this.upsertCreateInTenantUserRoles({
|
|
572
|
+
userId: options.userId,
|
|
573
|
+
tenantId: options.tenantId,
|
|
574
|
+
applicationId: options.tenantMembershipCreate
|
|
575
|
+
? undefined
|
|
576
|
+
: options.sessionApplicationId,
|
|
577
|
+
roleIds: options.roleIds,
|
|
578
|
+
});
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
upsertCreateInTenantUserRoles(options) {
|
|
582
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
583
|
+
var _a;
|
|
584
|
+
if (options.roleIds.length === 0) {
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
const scopeInfo = Object.assign({ tenantId: options.tenantId }, (((_a = options.applicationId) === null || _a === void 0 ? void 0 : _a.trim())
|
|
588
|
+
? { applicationId: options.applicationId.trim() }
|
|
589
|
+
: {}));
|
|
590
|
+
const existing = yield this.findUserRoleGrant(options.userId, options.tenantId, options.applicationId);
|
|
591
|
+
const existingId = readGrantId(existing);
|
|
592
|
+
if (existing && existingId) {
|
|
593
|
+
const state = existing['stateInfo'];
|
|
594
|
+
if ((state === null || state === void 0 ? void 0 : state.isSystem) === true || (state === null || state === void 0 ? void 0 : state.isImmutable) === true) {
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
yield this.entityStorage.updateOneAsEngine(USER_ROLE_ENTITY, existingId, {
|
|
598
|
+
roleIds: options.roleIds,
|
|
599
|
+
scopeInfo,
|
|
600
|
+
});
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
yield this.entityStorage.insertOneAsEngine(USER_ROLE_ENTITY, {
|
|
604
|
+
id: (0, crypto_1.randomUUID)(),
|
|
605
|
+
userId: options.userId,
|
|
606
|
+
roleIds: options.roleIds,
|
|
607
|
+
scopeInfo,
|
|
608
|
+
});
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
//#endregion
|
|
612
|
+
//#region ---- Invitee flows ----
|
|
613
|
+
/**
|
|
614
|
+
* Public page load. Does not write a password, clear the token, or change status.
|
|
615
|
+
* Unknown tokens (already accepted, resent, or never issued) are `invalid`.
|
|
616
|
+
*/
|
|
617
|
+
previewInvitation(token) {
|
|
618
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
619
|
+
var _a;
|
|
620
|
+
const trimmed = token === null || token === void 0 ? void 0 : token.trim();
|
|
621
|
+
if (!trimmed) {
|
|
622
|
+
return { usable: false, reason: 'invalid' };
|
|
623
|
+
}
|
|
624
|
+
const membership = yield this.findMembershipByTokenHash(hashToken(trimmed));
|
|
625
|
+
if (!(membership === null || membership === void 0 ? void 0 : membership.id) ||
|
|
626
|
+
!((_a = membership.invitation) === null || _a === void 0 ? void 0 : _a.tokenHash) ||
|
|
627
|
+
membership.statusId !== core_2.AXPSystemStatusType.Pending) {
|
|
628
|
+
return { usable: false, reason: 'invalid' };
|
|
629
|
+
}
|
|
630
|
+
const expiresAt = Date.parse(membership.invitation.expiresAt);
|
|
631
|
+
if (!Number.isFinite(expiresAt) || expiresAt < Date.now()) {
|
|
632
|
+
return { usable: false, reason: 'expired' };
|
|
633
|
+
}
|
|
634
|
+
return { usable: true, reason: 'valid' };
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
acceptInvitation(input) {
|
|
638
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
639
|
+
var _a, _b;
|
|
640
|
+
const token = (_a = input.token) === null || _a === void 0 ? void 0 : _a.trim();
|
|
641
|
+
if (!token) {
|
|
642
|
+
return {
|
|
643
|
+
success: false,
|
|
644
|
+
code: module_security_management_contracts_1.AXM_ACCEPT_INVITATION_ERROR_CODES.Invalid,
|
|
645
|
+
message: 'Invitation token is required.',
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
const tokenHash = hashToken(token);
|
|
649
|
+
const membership = yield this.findMembershipByTokenHash(tokenHash);
|
|
650
|
+
const storedTokenHash = (_b = membership === null || membership === void 0 ? void 0 : membership.invitation) === null || _b === void 0 ? void 0 : _b.tokenHash;
|
|
651
|
+
if (!(membership === null || membership === void 0 ? void 0 : membership.id) || !storedTokenHash || !membership.userId) {
|
|
652
|
+
return {
|
|
653
|
+
success: false,
|
|
654
|
+
code: module_security_management_contracts_1.AXM_ACCEPT_INVITATION_ERROR_CODES.Invalid,
|
|
655
|
+
message: 'This invitation link is invalid or has expired.',
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
if (!tokensMatch(storedTokenHash, tokenHash)) {
|
|
659
|
+
return {
|
|
660
|
+
success: false,
|
|
661
|
+
code: module_security_management_contracts_1.AXM_ACCEPT_INVITATION_ERROR_CODES.Invalid,
|
|
662
|
+
message: 'This invitation link is invalid or has expired.',
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
return this.activateMembership(membership, input.newPassword);
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
listMyPendingInvitations() {
|
|
669
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
670
|
+
var _a, _b, _c;
|
|
671
|
+
const session = this.requireSession();
|
|
672
|
+
const userId = (_a = session.userId) === null || _a === void 0 ? void 0 : _a.trim();
|
|
673
|
+
if (!userId) {
|
|
674
|
+
return { success: false, message: 'Not signed in.' };
|
|
675
|
+
}
|
|
676
|
+
const items = [];
|
|
677
|
+
for (const membership of yield this.listTenantUsersForUser(userId)) {
|
|
678
|
+
if (membership.statusId !== core_2.AXPSystemStatusType.Pending ||
|
|
679
|
+
!((_b = membership.invitation) === null || _b === void 0 ? void 0 : _b.tokenHash) ||
|
|
680
|
+
!membership.id ||
|
|
681
|
+
!membership.tenantId) {
|
|
682
|
+
continue;
|
|
683
|
+
}
|
|
684
|
+
const tenant = yield this.getJoinableTenantById(membership.tenantId);
|
|
685
|
+
items.push({
|
|
686
|
+
tenantUserId: membership.id,
|
|
687
|
+
tenantId: membership.tenantId,
|
|
688
|
+
tenantTitle: (_c = tenant === null || tenant === void 0 ? void 0 : tenant.title) !== null && _c !== void 0 ? _c : (yield this.resolveTenantTitle(membership.tenantId)),
|
|
689
|
+
tenantLogo: tenant === null || tenant === void 0 ? void 0 : tenant.logo,
|
|
690
|
+
invitedByUserId: membership.invitation.invitedByUserId,
|
|
691
|
+
expiresAt: membership.invitation.expiresAt,
|
|
692
|
+
sentAt: membership.invitation.sentAt,
|
|
693
|
+
applicationIds: membership.invitation.applicationIds,
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
return { success: true, data: { items } };
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
acceptMyInvitation(input) {
|
|
700
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
701
|
+
var _a;
|
|
702
|
+
const session = this.requireSession();
|
|
703
|
+
const userId = (_a = session.userId) === null || _a === void 0 ? void 0 : _a.trim();
|
|
704
|
+
if (!userId) {
|
|
705
|
+
return { success: false, message: 'Not signed in.' };
|
|
706
|
+
}
|
|
707
|
+
const membership = yield this.findOwnMembershipById(userId, input.tenantUserId);
|
|
708
|
+
if (!(membership === null || membership === void 0 ? void 0 : membership.id)) {
|
|
709
|
+
return { success: false, message: 'Invitation not found.' };
|
|
710
|
+
}
|
|
711
|
+
if (membership.statusId !== core_2.AXPSystemStatusType.Pending || !membership.invitation) {
|
|
712
|
+
return { success: false, message: 'Invitation is not pending.' };
|
|
713
|
+
}
|
|
714
|
+
return this.activateMembership(membership, input.newPassword);
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
declineInvitation(input) {
|
|
718
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
719
|
+
var _a;
|
|
720
|
+
const session = this.requireSession();
|
|
721
|
+
const userId = (_a = session.userId) === null || _a === void 0 ? void 0 : _a.trim();
|
|
722
|
+
if (!userId) {
|
|
723
|
+
return { success: false, message: 'Not signed in.' };
|
|
724
|
+
}
|
|
725
|
+
const membership = yield this.findOwnMembershipById(userId, input.tenantUserId);
|
|
726
|
+
if (!(membership === null || membership === void 0 ? void 0 : membership.id)) {
|
|
727
|
+
return { success: false, message: 'Invitation not found.' };
|
|
728
|
+
}
|
|
729
|
+
if (membership.statusId !== core_2.AXPSystemStatusType.Pending || !membership.invitation) {
|
|
730
|
+
return { success: false, message: 'Invitation is not pending.' };
|
|
731
|
+
}
|
|
732
|
+
yield this.resolveMembershipStatus(membership, core_2.AXPSystemStatusType.Rejected);
|
|
733
|
+
return { success: true };
|
|
734
|
+
});
|
|
735
|
+
}
|
|
736
|
+
lookupTenantByUniqueName(input) {
|
|
737
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
738
|
+
var _a, _b, _c;
|
|
739
|
+
const session = this.requireSession();
|
|
740
|
+
if (!((_a = session.userId) === null || _a === void 0 ? void 0 : _a.trim())) {
|
|
741
|
+
return {
|
|
742
|
+
success: false,
|
|
743
|
+
code: module_security_management_contracts_1.AXM_JOIN_REQUEST_ERROR_CODES.NotSignedIn,
|
|
744
|
+
message: 'Not signed in.',
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
const uniqueName = (_c = (_b = input.uniqueName) === null || _b === void 0 ? void 0 : _b.trim()) !== null && _c !== void 0 ? _c : '';
|
|
748
|
+
if (!uniqueName) {
|
|
749
|
+
return { success: true, data: { item: null } };
|
|
750
|
+
}
|
|
751
|
+
const tenant = yield this.findActiveTenantByUniqueName(uniqueName);
|
|
752
|
+
if (!tenant) {
|
|
753
|
+
return { success: true, data: { item: null } };
|
|
754
|
+
}
|
|
755
|
+
return {
|
|
756
|
+
success: true,
|
|
757
|
+
data: {
|
|
758
|
+
item: { id: tenant.id, name: tenant.name },
|
|
759
|
+
},
|
|
760
|
+
};
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
requestTenantMembership(input) {
|
|
764
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
765
|
+
var _a, _b, _c, _d;
|
|
766
|
+
const session = this.requireSession();
|
|
767
|
+
const userId = (_a = session.userId) === null || _a === void 0 ? void 0 : _a.trim();
|
|
768
|
+
if (!userId) {
|
|
769
|
+
return {
|
|
770
|
+
success: false,
|
|
771
|
+
code: module_security_management_contracts_1.AXM_JOIN_REQUEST_ERROR_CODES.NotSignedIn,
|
|
772
|
+
message: 'Not signed in.',
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
const uniqueName = (_c = (_b = input.uniqueName) === null || _b === void 0 ? void 0 : _b.trim()) !== null && _c !== void 0 ? _c : '';
|
|
776
|
+
if (!uniqueName) {
|
|
777
|
+
return {
|
|
778
|
+
success: false,
|
|
779
|
+
code: module_security_management_contracts_1.AXM_JOIN_REQUEST_ERROR_CODES.InvalidInput,
|
|
780
|
+
message: 'Organization unique name is required.',
|
|
781
|
+
};
|
|
782
|
+
}
|
|
783
|
+
const tenant = yield this.findActiveTenantByUniqueName(uniqueName);
|
|
784
|
+
if (!tenant) {
|
|
785
|
+
return {
|
|
786
|
+
success: false,
|
|
787
|
+
code: module_security_management_contracts_1.AXM_JOIN_REQUEST_ERROR_CODES.TenantNotFound,
|
|
788
|
+
message: 'No organization matches that unique name.',
|
|
789
|
+
};
|
|
790
|
+
}
|
|
791
|
+
const existing = yield this.findTenantUser(tenant.id, userId);
|
|
792
|
+
if ((existing === null || existing === void 0 ? void 0 : existing.statusId) === core_2.AXPSystemStatusType.Active) {
|
|
793
|
+
return {
|
|
794
|
+
success: false,
|
|
795
|
+
code: module_security_management_contracts_1.AXM_JOIN_REQUEST_ERROR_CODES.AlreadyMember,
|
|
796
|
+
message: 'You are already a member of this organization.',
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
if ((existing === null || existing === void 0 ? void 0 : existing.statusId) === core_2.AXPSystemStatusType.Pending &&
|
|
800
|
+
((_d = existing.invitation) === null || _d === void 0 ? void 0 : _d.source) === module_security_management_contracts_1.AXM_MEMBERSHIP_INVITATION_SOURCE.Invite) {
|
|
801
|
+
return {
|
|
802
|
+
success: false,
|
|
803
|
+
code: module_security_management_contracts_1.AXM_JOIN_REQUEST_ERROR_CODES.InvitePending,
|
|
804
|
+
message: 'You already have a pending invitation for this organization.',
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
if (this.isPendingJoinRequest(existing) && (existing === null || existing === void 0 ? void 0 : existing.id)) {
|
|
808
|
+
return {
|
|
809
|
+
success: true,
|
|
810
|
+
data: {
|
|
811
|
+
tenantUserId: existing.id,
|
|
812
|
+
tenantId: tenant.id,
|
|
813
|
+
tenantName: tenant.name,
|
|
814
|
+
},
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
const now = new Date();
|
|
818
|
+
const invitation = {
|
|
819
|
+
expiresAt: new Date(now.getTime() + JOIN_REQUEST_EXPIRY_MINUTES * 60000).toISOString(),
|
|
820
|
+
sentAt: now.toISOString(),
|
|
821
|
+
invitedByUserId: userId,
|
|
822
|
+
applicationIds: [],
|
|
823
|
+
source: module_security_management_contracts_1.AXM_MEMBERSHIP_INVITATION_SOURCE.JoinRequest,
|
|
824
|
+
};
|
|
825
|
+
const tenantUserId = yield this.upsertPendingTenantUser(tenant.id, userId, invitation);
|
|
826
|
+
return {
|
|
827
|
+
success: true,
|
|
828
|
+
data: {
|
|
829
|
+
tenantUserId,
|
|
830
|
+
tenantId: tenant.id,
|
|
831
|
+
tenantName: tenant.name,
|
|
832
|
+
},
|
|
833
|
+
};
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
listMyJoinRequests() {
|
|
837
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
838
|
+
var _a, _b, _c, _d;
|
|
839
|
+
const session = this.requireSession();
|
|
840
|
+
const userId = (_a = session.userId) === null || _a === void 0 ? void 0 : _a.trim();
|
|
841
|
+
if (!userId) {
|
|
842
|
+
return {
|
|
843
|
+
success: false,
|
|
844
|
+
code: module_security_management_contracts_1.AXM_JOIN_REQUEST_ERROR_CODES.NotSignedIn,
|
|
845
|
+
message: 'Not signed in.',
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
const items = [];
|
|
849
|
+
for (const membership of yield this.listTenantUsersForUser(userId)) {
|
|
850
|
+
if (!this.isPendingJoinRequest(membership) || !membership.id || !membership.tenantId) {
|
|
851
|
+
continue;
|
|
852
|
+
}
|
|
853
|
+
const tenant = yield this.getJoinableTenantById(membership.tenantId);
|
|
854
|
+
items.push({
|
|
855
|
+
tenantUserId: membership.id,
|
|
856
|
+
tenantId: membership.tenantId,
|
|
857
|
+
tenantName: tenant === null || tenant === void 0 ? void 0 : tenant.name,
|
|
858
|
+
tenantTitle: (_b = tenant === null || tenant === void 0 ? void 0 : tenant.title) !== null && _b !== void 0 ? _b : (yield this.resolveTenantTitle(membership.tenantId)),
|
|
859
|
+
tenantLogo: tenant === null || tenant === void 0 ? void 0 : tenant.logo,
|
|
860
|
+
sentAt: (_c = membership.invitation) === null || _c === void 0 ? void 0 : _c.sentAt,
|
|
861
|
+
expiresAt: (_d = membership.invitation) === null || _d === void 0 ? void 0 : _d.expiresAt,
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
return { success: true, data: { items } };
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
cancelMyJoinRequest(input) {
|
|
868
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
869
|
+
var _a;
|
|
870
|
+
const session = this.requireSession();
|
|
871
|
+
const userId = (_a = session.userId) === null || _a === void 0 ? void 0 : _a.trim();
|
|
872
|
+
if (!userId) {
|
|
873
|
+
return {
|
|
874
|
+
success: false,
|
|
875
|
+
code: module_security_management_contracts_1.AXM_JOIN_REQUEST_ERROR_CODES.NotSignedIn,
|
|
876
|
+
message: 'Not signed in.',
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
const membership = yield this.findOwnMembershipById(userId, input.tenantUserId);
|
|
880
|
+
if (!(membership === null || membership === void 0 ? void 0 : membership.id)) {
|
|
881
|
+
return {
|
|
882
|
+
success: false,
|
|
883
|
+
code: module_security_management_contracts_1.AXM_JOIN_REQUEST_ERROR_CODES.RequestNotFound,
|
|
884
|
+
message: 'Join request not found.',
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
if (!this.isPendingJoinRequest(membership)) {
|
|
888
|
+
return {
|
|
889
|
+
success: false,
|
|
890
|
+
code: module_security_management_contracts_1.AXM_JOIN_REQUEST_ERROR_CODES.NotPending,
|
|
891
|
+
message: 'Join request is not pending.',
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
yield this.resolveMembershipStatus(membership, core_2.AXPSystemStatusType.Cancelled);
|
|
895
|
+
return { success: true };
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
//#endregion
|
|
899
|
+
//#region ---- Private helpers ----
|
|
900
|
+
activateMembership(membership, newPassword) {
|
|
901
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
902
|
+
var _a, _b;
|
|
903
|
+
if (!membership.id || !membership.userId || !membership.invitation) {
|
|
904
|
+
return {
|
|
905
|
+
success: false,
|
|
906
|
+
code: module_security_management_contracts_1.AXM_ACCEPT_INVITATION_ERROR_CODES.Invalid,
|
|
907
|
+
message: 'This invitation link is invalid or has expired.',
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
const expiresAt = Date.parse(membership.invitation.expiresAt);
|
|
911
|
+
if (!Number.isFinite(expiresAt) || expiresAt < Date.now()) {
|
|
912
|
+
yield this.resolveMembershipStatus(membership, core_2.AXPSystemStatusType.Expired);
|
|
913
|
+
return {
|
|
914
|
+
success: false,
|
|
915
|
+
code: module_security_management_contracts_1.AXM_ACCEPT_INVITATION_ERROR_CODES.Expired,
|
|
916
|
+
message: 'This invitation link is invalid or has expired.',
|
|
917
|
+
};
|
|
918
|
+
}
|
|
919
|
+
const user = (yield this.entityStorage.getOneAsEngine(USER_ENTITY, membership.userId));
|
|
920
|
+
if (!(user === null || user === void 0 ? void 0 : user.id)) {
|
|
921
|
+
return { success: false, message: 'User not found for this invitation.' };
|
|
922
|
+
}
|
|
923
|
+
// Invite-created identities stay pending until they choose a password.
|
|
924
|
+
// Existing Active users (e.g. independent register) already have credentials.
|
|
925
|
+
const needsPassword = user.accountState === PENDING_ACCOUNT_STATE;
|
|
926
|
+
const homeTenantId = typeof user.homeTenantId === 'string' ? user.homeTenantId.trim() : '';
|
|
927
|
+
const identityPatch = {};
|
|
928
|
+
if (!homeTenantId && membership.tenantId) {
|
|
929
|
+
identityPatch['homeTenantId'] = membership.tenantId;
|
|
930
|
+
}
|
|
931
|
+
if (needsPassword) {
|
|
932
|
+
const password = newPassword !== null && newPassword !== void 0 ? newPassword : '';
|
|
933
|
+
if (password.length < 6) {
|
|
934
|
+
return {
|
|
935
|
+
success: false,
|
|
936
|
+
code: module_security_management_contracts_1.AXM_ACCEPT_INVITATION_ERROR_CODES.PasswordRequired,
|
|
937
|
+
message: 'A new password of at least 6 characters is required.',
|
|
938
|
+
};
|
|
939
|
+
}
|
|
940
|
+
yield this.entityStorage.updateOneAsEngine(USER_ENTITY, user.id, Object.assign(Object.assign({}, identityPatch), { password: yield (0, core_2.hashPassword)(password), passwordChangedAt: new Date().toISOString(), accountState: ACTIVE_ACCOUNT_STATE, accessState: module_security_management_contracts_1.AXMUserAccessState.Allowed }));
|
|
941
|
+
}
|
|
942
|
+
else if (user.accountState !== ACTIVE_ACCOUNT_STATE || Object.keys(identityPatch).length > 0) {
|
|
943
|
+
yield this.entityStorage.updateOneAsEngine(USER_ENTITY, user.id, Object.assign(Object.assign({}, identityPatch), (user.accountState !== ACTIVE_ACCOUNT_STATE
|
|
944
|
+
? {
|
|
945
|
+
accountState: ACTIVE_ACCOUNT_STATE,
|
|
946
|
+
accessState: module_security_management_contracts_1.AXMUserAccessState.Allowed,
|
|
947
|
+
}
|
|
948
|
+
: {})));
|
|
949
|
+
}
|
|
950
|
+
const acceptedAt = new Date().toISOString();
|
|
951
|
+
yield this.entityStorage.updateOneAsEngine(TENANT_USER_ENTITY, membership.id, {
|
|
952
|
+
statusId: core_2.AXPSystemStatusType.Active,
|
|
953
|
+
invitation: invitationWithoutToken(membership.invitation, { acceptedAt }),
|
|
954
|
+
endDate: null,
|
|
955
|
+
});
|
|
956
|
+
const tenantIdForAccess = String((_a = membership.tenantId) !== null && _a !== void 0 ? _a : '').trim();
|
|
957
|
+
const entitledIds = tenantIdForAccess
|
|
958
|
+
? new Set(yield (0, user_invitation_application_access_util_1.resolveTenantSubscribedApplicationIds)(this.entityStorage, tenantIdForAccess))
|
|
959
|
+
: new Set();
|
|
960
|
+
const tenant = tenantIdForAccess
|
|
961
|
+
? (yield this.entityStorage.getOneAsEngine(TENANT_ENTITY, tenantIdForAccess))
|
|
962
|
+
: null;
|
|
963
|
+
const tenantActive = Boolean(tenant === null || tenant === void 0 ? void 0 : tenant['id']) && (tenant === null || tenant === void 0 ? void 0 : tenant['statusId']) !== core_2.AXPSystemStatusType.Inactive;
|
|
964
|
+
for (const row of yield this.listAppAccess(membership.id)) {
|
|
965
|
+
if (!row.id) {
|
|
966
|
+
continue;
|
|
967
|
+
}
|
|
968
|
+
const applicationId = String((_b = row.applicationId) !== null && _b !== void 0 ? _b : '').trim();
|
|
969
|
+
const stillAllowed = tenantActive && applicationId.length > 0 && entitledIds.has(applicationId);
|
|
970
|
+
if (stillAllowed) {
|
|
971
|
+
yield this.entityStorage.updateOneAsEngine(APP_ACCESS_ENTITY, row.id, {
|
|
972
|
+
statusId: core_2.AXPSystemStatusType.Active,
|
|
973
|
+
endDate: null,
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
else {
|
|
977
|
+
// Do not activate revoked/invalid AppAccess (ADR-TEN-001 D-06).
|
|
978
|
+
yield this.entityStorage.updateOneAsEngine(APP_ACCESS_ENTITY, row.id, {
|
|
979
|
+
statusId: core_2.AXPSystemStatusType.Cancelled,
|
|
980
|
+
endDate: new Date().toISOString(),
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
return { success: true };
|
|
985
|
+
});
|
|
986
|
+
}
|
|
987
|
+
isPendingJoinRequest(membership) {
|
|
988
|
+
var _a;
|
|
989
|
+
return (!!(membership === null || membership === void 0 ? void 0 : membership.id) &&
|
|
990
|
+
membership.statusId === core_2.AXPSystemStatusType.Pending &&
|
|
991
|
+
((_a = membership.invitation) === null || _a === void 0 ? void 0 : _a.source) === module_security_management_contracts_1.AXM_MEMBERSHIP_INVITATION_SOURCE.JoinRequest);
|
|
992
|
+
}
|
|
993
|
+
isJoinableTenant(row) {
|
|
994
|
+
var _a;
|
|
995
|
+
if (!(row === null || row === void 0 ? void 0 : row['id'])) {
|
|
996
|
+
return false;
|
|
997
|
+
}
|
|
998
|
+
const statusId = String((_a = row['statusId']) !== null && _a !== void 0 ? _a : '');
|
|
999
|
+
return statusId !== core_2.AXPSystemStatusType.Inactive;
|
|
1000
|
+
}
|
|
1001
|
+
/** Exact case-insensitive match on unique `name` or any locale value of display `title`. */
|
|
1002
|
+
tenantNameOrTitleMatches(row, needle) {
|
|
1003
|
+
var _a;
|
|
1004
|
+
const name = String((_a = row['name']) !== null && _a !== void 0 ? _a : '').trim().toLowerCase();
|
|
1005
|
+
if (name === needle) {
|
|
1006
|
+
return true;
|
|
1007
|
+
}
|
|
1008
|
+
const title = (0, core_2.coerceUnknownToMultiLanguageString)(row['title']);
|
|
1009
|
+
if (typeof title === 'string') {
|
|
1010
|
+
return title.trim().toLowerCase() === needle;
|
|
1011
|
+
}
|
|
1012
|
+
if ((0, core_2.isLocaleStringMap)(title)) {
|
|
1013
|
+
return Object.values(title).some((value) => typeof value === 'string' && value.trim().toLowerCase() === needle);
|
|
1014
|
+
}
|
|
1015
|
+
return false;
|
|
1016
|
+
}
|
|
1017
|
+
findActiveTenantByUniqueName(uniqueName) {
|
|
1018
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1019
|
+
var _a;
|
|
1020
|
+
const needle = uniqueName.trim().toLowerCase();
|
|
1021
|
+
if (!needle) {
|
|
1022
|
+
return null;
|
|
1023
|
+
}
|
|
1024
|
+
const result = yield this.entityStorage.query(TENANT_ENTITY, (0, abstract_1.bypassAllEntityVisibilityFilters)({ skip: 0, take: 5000 }));
|
|
1025
|
+
const items = (0, lodash_1.get)(result, 'items', []);
|
|
1026
|
+
const matches = (Array.isArray(items) ? items : []).filter((row) => this.isJoinableTenant(row) && this.tenantNameOrTitleMatches(row, needle));
|
|
1027
|
+
if (matches.length !== 1) {
|
|
1028
|
+
return null;
|
|
1029
|
+
}
|
|
1030
|
+
const row = matches[0];
|
|
1031
|
+
return {
|
|
1032
|
+
id: String(row['id']),
|
|
1033
|
+
name: String((_a = row['name']) !== null && _a !== void 0 ? _a : '').trim(),
|
|
1034
|
+
};
|
|
1035
|
+
});
|
|
1036
|
+
}
|
|
1037
|
+
getJoinableTenantById(tenantId) {
|
|
1038
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1039
|
+
var _a;
|
|
1040
|
+
try {
|
|
1041
|
+
const row = (yield this.entityStorage.getOneAsEngine(TENANT_ENTITY, tenantId));
|
|
1042
|
+
if (!this.isJoinableTenant(row) || !row) {
|
|
1043
|
+
return null;
|
|
1044
|
+
}
|
|
1045
|
+
return {
|
|
1046
|
+
id: String(row['id']),
|
|
1047
|
+
name: String((_a = row['name']) !== null && _a !== void 0 ? _a : '').trim(),
|
|
1048
|
+
title: this.extractTenantTitle(row['title']),
|
|
1049
|
+
logo: this.extractTenantLogo(row['logo']),
|
|
1050
|
+
};
|
|
1051
|
+
}
|
|
1052
|
+
catch (error) {
|
|
1053
|
+
if (error instanceof common_1.NotFoundException) {
|
|
1054
|
+
return null;
|
|
1055
|
+
}
|
|
1056
|
+
throw error;
|
|
1057
|
+
}
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
1060
|
+
extractTenantTitle(title) {
|
|
1061
|
+
if (typeof title === 'string' && title.trim()) {
|
|
1062
|
+
return title.trim();
|
|
1063
|
+
}
|
|
1064
|
+
const mls = (0, core_2.coerceUnknownToMultiLanguageString)(title);
|
|
1065
|
+
if (typeof mls === 'string' && mls.trim()) {
|
|
1066
|
+
return mls.trim();
|
|
1067
|
+
}
|
|
1068
|
+
if ((0, core_2.isLocaleStringMap)(mls)) {
|
|
1069
|
+
return (mls['en-US'] ||
|
|
1070
|
+
mls['en'] ||
|
|
1071
|
+
mls['fa-IR'] ||
|
|
1072
|
+
Object.values(mls).find((value) => typeof value === 'string' && value.trim()) ||
|
|
1073
|
+
undefined);
|
|
1074
|
+
}
|
|
1075
|
+
return undefined;
|
|
1076
|
+
}
|
|
1077
|
+
extractTenantLogo(logo) {
|
|
1078
|
+
if (typeof logo === 'string' && logo.trim()) {
|
|
1079
|
+
return logo.trim();
|
|
1080
|
+
}
|
|
1081
|
+
if (logo && typeof logo === 'object' && 'url' in logo) {
|
|
1082
|
+
const url = logo.url;
|
|
1083
|
+
if (typeof url === 'string' && url.trim()) {
|
|
1084
|
+
return url.trim();
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
return undefined;
|
|
1088
|
+
}
|
|
1089
|
+
requireSession() {
|
|
1090
|
+
const request = (0, core_1.getEntityStorageRequest)();
|
|
1091
|
+
const session = request === null || request === void 0 ? void 0 : request[security_1.CURRENT_SESSION_KEY];
|
|
1092
|
+
if (!(session === null || session === void 0 ? void 0 : session.userId)) {
|
|
1093
|
+
throw new Error('Request is not authenticated.');
|
|
1094
|
+
}
|
|
1095
|
+
return session;
|
|
1096
|
+
}
|
|
1097
|
+
/**
|
|
1098
|
+
* Product / app-scoped invite: force session application only.
|
|
1099
|
+
* Tenant Users multi-app: `tenantMembershipInvite` + session edition feature,
|
|
1100
|
+
* intersected with tenant subscription entitlement (ADR-TEN-001 D-05).
|
|
1101
|
+
*/
|
|
1102
|
+
resolveAuthorizedApplicationIds(inputIds, session, tenantId, tenantMembershipInvite) {
|
|
1103
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1104
|
+
var _a;
|
|
1105
|
+
const entitled = yield (0, user_invitation_application_access_util_1.resolveTenantSubscribedApplicationIds)(this.entityStorage, tenantId);
|
|
1106
|
+
const entitledSet = new Set(entitled);
|
|
1107
|
+
const allowMulti = tenantMembershipInvite &&
|
|
1108
|
+
(yield (0, user_invitation_application_access_util_1.sessionApplicationHasFeature)(this.entityStorage, session.applicationId, user_invitation_application_access_util_1.INVITE_MULTI_APPLICATION_FEATURE));
|
|
1109
|
+
if (!allowMulti) {
|
|
1110
|
+
const sessionApp = (_a = session.applicationId) === null || _a === void 0 ? void 0 : _a.trim();
|
|
1111
|
+
if (!sessionApp) {
|
|
1112
|
+
return [];
|
|
1113
|
+
}
|
|
1114
|
+
return (0, user_invitation_application_access_util_1.filterApplicationIdsByEntitlement)([sessionApp], entitledSet);
|
|
1115
|
+
}
|
|
1116
|
+
const requested = this.resolveApplicationIds(inputIds, session.applicationId);
|
|
1117
|
+
return (0, user_invitation_application_access_util_1.filterApplicationIdsByEntitlement)(requested, entitledSet);
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
listTenantEntitledApplications() {
|
|
1121
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1122
|
+
var _a;
|
|
1123
|
+
const session = this.requireSession();
|
|
1124
|
+
const tenantId = (_a = session.tenantId) === null || _a === void 0 ? void 0 : _a.trim();
|
|
1125
|
+
if (!tenantId) {
|
|
1126
|
+
return { success: false, message: 'Tenant context is required.' };
|
|
1127
|
+
}
|
|
1128
|
+
const items = yield (0, user_invitation_application_access_util_1.resolveTenantSubscribedApplications)(this.entityStorage, tenantId);
|
|
1129
|
+
return { success: true, data: { items } };
|
|
1130
|
+
});
|
|
1131
|
+
}
|
|
1132
|
+
/**
|
|
1133
|
+
* TM Tenant Users invite repeater only — overlay roles for the current tenant.
|
|
1134
|
+
* Does not change Role list isolation or Security → Users invite.
|
|
1135
|
+
*/
|
|
1136
|
+
listInviteAssignableRoles(applicationId) {
|
|
1137
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1138
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
1139
|
+
const session = this.requireSession();
|
|
1140
|
+
const tenantId = (_a = session.tenantId) === null || _a === void 0 ? void 0 : _a.trim();
|
|
1141
|
+
if (!tenantId) {
|
|
1142
|
+
return { success: false, message: 'Tenant context is required.' };
|
|
1143
|
+
}
|
|
1144
|
+
const selectedApplicationId = applicationId === null || applicationId === void 0 ? void 0 : applicationId.trim();
|
|
1145
|
+
const entitled = new Set(yield (0, user_invitation_application_access_util_1.resolveTenantSubscribedApplicationIds)(this.entityStorage, tenantId));
|
|
1146
|
+
const filters = [
|
|
1147
|
+
{ field: 'scopeInfo.tenantId', operator: { type: 'equal' }, value: tenantId },
|
|
1148
|
+
];
|
|
1149
|
+
if (selectedApplicationId) {
|
|
1150
|
+
filters.push({
|
|
1151
|
+
field: 'scopeInfo.applicationId',
|
|
1152
|
+
operator: { type: 'equal' },
|
|
1153
|
+
value: selectedApplicationId,
|
|
1154
|
+
});
|
|
1155
|
+
}
|
|
1156
|
+
const result = yield this.entityStorage.query(ROLE_ENTITY, (0, abstract_1.bypassAllEntityVisibilityFilters)({
|
|
1157
|
+
skip: 0,
|
|
1158
|
+
take: 500,
|
|
1159
|
+
filter: { logic: 'and', filters },
|
|
1160
|
+
}));
|
|
1161
|
+
const rows = (0, lodash_1.get)(result, 'items', []);
|
|
1162
|
+
const items = [];
|
|
1163
|
+
for (const row of Array.isArray(rows) ? rows : []) {
|
|
1164
|
+
const id = String((_c = (_b = row['id']) !== null && _b !== void 0 ? _b : row['_id']) !== null && _c !== void 0 ? _c : '').trim();
|
|
1165
|
+
if (!id) {
|
|
1166
|
+
continue;
|
|
1167
|
+
}
|
|
1168
|
+
const applicationId = String((_e = (_d = row['scopeInfo']) === null || _d === void 0 ? void 0 : _d['applicationId']) !== null && _e !== void 0 ? _e : '').trim();
|
|
1169
|
+
if (!applicationId || (entitled.size > 0 && !entitled.has(applicationId))) {
|
|
1170
|
+
continue;
|
|
1171
|
+
}
|
|
1172
|
+
if (selectedApplicationId && applicationId !== selectedApplicationId) {
|
|
1173
|
+
continue;
|
|
1174
|
+
}
|
|
1175
|
+
if (!(0, module_security_management_contracts_1.isRoleAssignableForTenantUserInviteRow)(row, {
|
|
1176
|
+
tenantId,
|
|
1177
|
+
applicationId,
|
|
1178
|
+
})) {
|
|
1179
|
+
continue;
|
|
1180
|
+
}
|
|
1181
|
+
const name = typeof row['name'] === 'string' ? row['name'] : undefined;
|
|
1182
|
+
items.push({
|
|
1183
|
+
id,
|
|
1184
|
+
title: (_g = (_f = row['title']) !== null && _f !== void 0 ? _f : name) !== null && _g !== void 0 ? _g : id,
|
|
1185
|
+
name,
|
|
1186
|
+
applicationId,
|
|
1187
|
+
});
|
|
1188
|
+
}
|
|
1189
|
+
return { success: true, data: { items } };
|
|
1190
|
+
});
|
|
1191
|
+
}
|
|
1192
|
+
revalidateApplicationIds(tenantId, applicationIds) {
|
|
1193
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1194
|
+
const entitled = yield (0, user_invitation_application_access_util_1.resolveTenantSubscribedApplicationIds)(this.entityStorage, tenantId);
|
|
1195
|
+
return (0, user_invitation_application_access_util_1.filterApplicationIdsByEntitlement)(applicationIds, entitled);
|
|
1196
|
+
});
|
|
1197
|
+
}
|
|
1198
|
+
/**
|
|
1199
|
+
* TM Tenant Users repeater: keep role ids that are overlays for tenant + that row's application.
|
|
1200
|
+
* Does not change {@link resolveAuthorizedRoleIds} (Security → Users / tenant-wide invite).
|
|
1201
|
+
*/
|
|
1202
|
+
resolveAuthorizedTenantUserInviteRoleIds(inputIds, tenantId, applicationId) {
|
|
1203
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1204
|
+
const requested = [
|
|
1205
|
+
...new Set((inputIds !== null && inputIds !== void 0 ? inputIds : []).map((id) => String(id).trim()).filter(Boolean)),
|
|
1206
|
+
];
|
|
1207
|
+
if (requested.length === 0) {
|
|
1208
|
+
return [];
|
|
1209
|
+
}
|
|
1210
|
+
const result = yield this.entityStorage.query(ROLE_ENTITY, (0, abstract_1.bypassAllEntityVisibilityFilters)({
|
|
1211
|
+
skip: 0,
|
|
1212
|
+
take: Math.max(requested.length, 50),
|
|
1213
|
+
filter: {
|
|
1214
|
+
logic: 'and',
|
|
1215
|
+
filters: [{ field: 'id', operator: { type: 'in' }, value: requested }],
|
|
1216
|
+
},
|
|
1217
|
+
}));
|
|
1218
|
+
const items = (0, lodash_1.get)(result, 'items', []);
|
|
1219
|
+
const byId = new Map((Array.isArray(items) ? items : [])
|
|
1220
|
+
.map((row) => {
|
|
1221
|
+
var _a, _b;
|
|
1222
|
+
const id = String((_b = (_a = row['id']) !== null && _a !== void 0 ? _a : row['_id']) !== null && _b !== void 0 ? _b : '').trim();
|
|
1223
|
+
return id ? [id, row] : null;
|
|
1224
|
+
})
|
|
1225
|
+
.filter((row) => row != null));
|
|
1226
|
+
return requested.filter((roleId) => {
|
|
1227
|
+
const role = byId.get(roleId);
|
|
1228
|
+
if (!role) {
|
|
1229
|
+
return false;
|
|
1230
|
+
}
|
|
1231
|
+
return (0, module_security_management_contracts_1.isRoleAssignableForTenantUserInviteRow)(role, { tenantId, applicationId });
|
|
1232
|
+
});
|
|
1233
|
+
});
|
|
1234
|
+
}
|
|
1235
|
+
/**
|
|
1236
|
+
* Intersect client roleIds with Role rows the inviter may assign for this invite mode.
|
|
1237
|
+
* Unknown / out-of-scope ids are dropped (not rejected) so invite still succeeds.
|
|
1238
|
+
*/
|
|
1239
|
+
resolveAuthorizedRoleIds(inputIds, session, tenantId, mode) {
|
|
1240
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1241
|
+
const requested = [
|
|
1242
|
+
...new Set((inputIds !== null && inputIds !== void 0 ? inputIds : []).map((id) => String(id).trim()).filter(Boolean)),
|
|
1243
|
+
];
|
|
1244
|
+
if (requested.length === 0) {
|
|
1245
|
+
return [];
|
|
1246
|
+
}
|
|
1247
|
+
const result = yield this.entityStorage.query(ROLE_ENTITY, (0, abstract_1.bypassAllEntityVisibilityFilters)({
|
|
1248
|
+
skip: 0,
|
|
1249
|
+
take: Math.max(requested.length, 50),
|
|
1250
|
+
filter: {
|
|
1251
|
+
logic: 'and',
|
|
1252
|
+
filters: [{ field: 'id', operator: { type: 'in' }, value: requested }],
|
|
1253
|
+
},
|
|
1254
|
+
}));
|
|
1255
|
+
const items = (0, lodash_1.get)(result, 'items', []);
|
|
1256
|
+
const byId = new Map((Array.isArray(items) ? items : [])
|
|
1257
|
+
.map((row) => {
|
|
1258
|
+
var _a, _b;
|
|
1259
|
+
const id = String((_b = (_a = row['id']) !== null && _a !== void 0 ? _a : row['_id']) !== null && _b !== void 0 ? _b : '').trim();
|
|
1260
|
+
return id ? [id, row] : null;
|
|
1261
|
+
})
|
|
1262
|
+
.filter((row) => row != null));
|
|
1263
|
+
const scopeApplicationId = mode === 'application' ? session.applicationId : undefined;
|
|
1264
|
+
return requested.filter((roleId) => {
|
|
1265
|
+
const role = byId.get(roleId);
|
|
1266
|
+
if (!role) {
|
|
1267
|
+
return false;
|
|
1268
|
+
}
|
|
1269
|
+
return (0, module_security_management_contracts_1.isRoleAssignableForInvite)(role, mode, {
|
|
1270
|
+
tenantId,
|
|
1271
|
+
applicationId: scopeApplicationId,
|
|
1272
|
+
});
|
|
1273
|
+
});
|
|
1274
|
+
});
|
|
1275
|
+
}
|
|
1276
|
+
upsertInviteUserRoles(options) {
|
|
1277
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1278
|
+
var _a;
|
|
1279
|
+
if (options.roleIds.length === 0) {
|
|
1280
|
+
return;
|
|
1281
|
+
}
|
|
1282
|
+
const scopeInfo = Object.assign({ tenantId: options.tenantId }, (((_a = options.applicationId) === null || _a === void 0 ? void 0 : _a.trim())
|
|
1283
|
+
? { applicationId: options.applicationId.trim() }
|
|
1284
|
+
: {}));
|
|
1285
|
+
const existing = yield this.findUserRoleGrant(options.userId, options.tenantId, options.applicationId);
|
|
1286
|
+
const existingId = readGrantId(existing);
|
|
1287
|
+
if (existing && existingId) {
|
|
1288
|
+
const state = existing['stateInfo'];
|
|
1289
|
+
if ((state === null || state === void 0 ? void 0 : state.isSystem) === true || (state === null || state === void 0 ? void 0 : state.isImmutable) === true) {
|
|
1290
|
+
return;
|
|
1291
|
+
}
|
|
1292
|
+
yield this.entityStorage.updateOneAsEngine(USER_ROLE_ENTITY, existingId, {
|
|
1293
|
+
roleIds: options.roleIds,
|
|
1294
|
+
scopeInfo,
|
|
1295
|
+
});
|
|
1296
|
+
return;
|
|
1297
|
+
}
|
|
1298
|
+
yield this.entityStorage.insertOneAsEngine(USER_ROLE_ENTITY, {
|
|
1299
|
+
id: (0, crypto_1.randomUUID)(),
|
|
1300
|
+
userId: options.userId,
|
|
1301
|
+
roleIds: options.roleIds,
|
|
1302
|
+
scopeInfo,
|
|
1303
|
+
});
|
|
1304
|
+
});
|
|
1305
|
+
}
|
|
1306
|
+
findUserRoleGrant(userId, tenantId, applicationId) {
|
|
1307
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1308
|
+
var _a;
|
|
1309
|
+
const result = yield this.entityStorage.query(USER_ROLE_ENTITY, (0, abstract_1.bypassAllEntityVisibilityFilters)({
|
|
1310
|
+
skip: 0,
|
|
1311
|
+
take: 50,
|
|
1312
|
+
filter: {
|
|
1313
|
+
logic: 'and',
|
|
1314
|
+
filters: [
|
|
1315
|
+
{ field: 'userId', operator: { type: 'equal' }, value: userId },
|
|
1316
|
+
{ field: 'scopeInfo.tenantId', operator: { type: 'equal' }, value: tenantId },
|
|
1317
|
+
],
|
|
1318
|
+
},
|
|
1319
|
+
}));
|
|
1320
|
+
const items = (0, lodash_1.get)(result, 'items', []);
|
|
1321
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
1322
|
+
return null;
|
|
1323
|
+
}
|
|
1324
|
+
const appId = applicationId === null || applicationId === void 0 ? void 0 : applicationId.trim();
|
|
1325
|
+
if (appId) {
|
|
1326
|
+
return ((_a = items.find((row) => {
|
|
1327
|
+
const scope = row['scopeInfo'];
|
|
1328
|
+
return (scope === null || scope === void 0 ? void 0 : scope['applicationId']) === appId;
|
|
1329
|
+
})) !== null && _a !== void 0 ? _a : null);
|
|
1330
|
+
}
|
|
1331
|
+
const tenantWide = items.find((row) => {
|
|
1332
|
+
const scope = row['scopeInfo'];
|
|
1333
|
+
const rowApp = scope === null || scope === void 0 ? void 0 : scope['applicationId'];
|
|
1334
|
+
return rowApp == null || rowApp === '';
|
|
1335
|
+
});
|
|
1336
|
+
return tenantWide !== null && tenantWide !== void 0 ? tenantWide : null;
|
|
1337
|
+
});
|
|
1338
|
+
}
|
|
1339
|
+
resolveApplicationIds(inputIds, sessionAppId) {
|
|
1340
|
+
const cleaned = [
|
|
1341
|
+
...new Set((inputIds !== null && inputIds !== void 0 ? inputIds : []).map((id) => id.trim()).filter(Boolean)),
|
|
1342
|
+
];
|
|
1343
|
+
if (cleaned.length > 0) {
|
|
1344
|
+
return cleaned;
|
|
1345
|
+
}
|
|
1346
|
+
const fallback = sessionAppId === null || sessionAppId === void 0 ? void 0 : sessionAppId.trim();
|
|
1347
|
+
return fallback ? [fallback] : [];
|
|
1348
|
+
}
|
|
1349
|
+
listUsers() {
|
|
1350
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1351
|
+
const result = yield this.entityStorage.query(USER_ENTITY, (0, abstract_1.bypassAllEntityVisibilityFilters)({ skip: 0, take: 5000 }));
|
|
1352
|
+
const items = (0, lodash_1.get)(result, 'items', []);
|
|
1353
|
+
return Array.isArray(items) ? items : [];
|
|
1354
|
+
});
|
|
1355
|
+
}
|
|
1356
|
+
findUserByEmail(email) {
|
|
1357
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1358
|
+
var _a;
|
|
1359
|
+
const normalized = email.trim().toLowerCase();
|
|
1360
|
+
return ((_a = (yield this.listUsers()).find((u) => {
|
|
1361
|
+
var _a, _b;
|
|
1362
|
+
const rowEmail = (_a = u.email) === null || _a === void 0 ? void 0 : _a.trim().toLowerCase();
|
|
1363
|
+
const rowUsername = (_b = u.username) === null || _b === void 0 ? void 0 : _b.trim().toLowerCase();
|
|
1364
|
+
return rowEmail === normalized || rowUsername === normalized;
|
|
1365
|
+
})) !== null && _a !== void 0 ? _a : null);
|
|
1366
|
+
});
|
|
1367
|
+
}
|
|
1368
|
+
findUserByUsername(username) {
|
|
1369
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1370
|
+
var _a;
|
|
1371
|
+
return ((_a = (yield this.listUsers()).find((u) => { var _a; return ((_a = u.username) === null || _a === void 0 ? void 0 : _a.trim().toLowerCase()) === username; })) !== null && _a !== void 0 ? _a : null);
|
|
1372
|
+
});
|
|
1373
|
+
}
|
|
1374
|
+
listTenantUsers() {
|
|
1375
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1376
|
+
const result = yield this.entityStorage.query(TENANT_USER_ENTITY, (0, abstract_1.bypassAllEntityVisibilityFilters)({ skip: 0, take: 10000 }));
|
|
1377
|
+
const items = (0, lodash_1.get)(result, 'items', []);
|
|
1378
|
+
return Array.isArray(items) ? items : [];
|
|
1379
|
+
});
|
|
1380
|
+
}
|
|
1381
|
+
listTenantUsersForUser(userId) {
|
|
1382
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1383
|
+
return (yield this.listTenantUsers()).filter((m) => m.userId === userId);
|
|
1384
|
+
});
|
|
1385
|
+
}
|
|
1386
|
+
findTenantUser(tenantId, userId) {
|
|
1387
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1388
|
+
var _a;
|
|
1389
|
+
return ((_a = (yield this.listTenantUsers()).find((m) => m.tenantId === tenantId && m.userId === userId)) !== null && _a !== void 0 ? _a : null);
|
|
1390
|
+
});
|
|
1391
|
+
}
|
|
1392
|
+
getTenantUserById(id) {
|
|
1393
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1394
|
+
var _a;
|
|
1395
|
+
try {
|
|
1396
|
+
return ((_a = (yield this.entityStorage.getOneAsEngine(TENANT_USER_ENTITY, id))) !== null && _a !== void 0 ? _a : null);
|
|
1397
|
+
}
|
|
1398
|
+
catch (error) {
|
|
1399
|
+
// Wrong id (e.g. User.id mistaken for TenantUser.id) must not 404 the command.
|
|
1400
|
+
if (error instanceof common_1.NotFoundException) {
|
|
1401
|
+
return null;
|
|
1402
|
+
}
|
|
1403
|
+
throw error;
|
|
1404
|
+
}
|
|
1405
|
+
});
|
|
1406
|
+
}
|
|
1407
|
+
/** Waiting-room callers have no tenant on the session; do not use request-scoped getOne. */
|
|
1408
|
+
findOwnMembershipById(userId, tenantUserId) {
|
|
1409
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1410
|
+
const id = tenantUserId === null || tenantUserId === void 0 ? void 0 : tenantUserId.trim();
|
|
1411
|
+
if (!id) {
|
|
1412
|
+
return null;
|
|
1413
|
+
}
|
|
1414
|
+
const row = yield this.getTenantUserById(id);
|
|
1415
|
+
return (row === null || row === void 0 ? void 0 : row.userId) === userId ? row : null;
|
|
1416
|
+
});
|
|
1417
|
+
}
|
|
1418
|
+
resolveMembership(tenantId, tenantUserId, userId) {
|
|
1419
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1420
|
+
if (tenantUserId === null || tenantUserId === void 0 ? void 0 : tenantUserId.trim()) {
|
|
1421
|
+
const row = yield this.getTenantUserById(tenantUserId.trim());
|
|
1422
|
+
if ((row === null || row === void 0 ? void 0 : row.tenantId) === tenantId) {
|
|
1423
|
+
return row;
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
if (userId === null || userId === void 0 ? void 0 : userId.trim()) {
|
|
1427
|
+
return this.findTenantUser(tenantId, userId.trim());
|
|
1428
|
+
}
|
|
1429
|
+
return null;
|
|
1430
|
+
});
|
|
1431
|
+
}
|
|
1432
|
+
findMembershipByTokenHash(tokenHash) {
|
|
1433
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1434
|
+
var _a;
|
|
1435
|
+
return ((_a = (yield this.listTenantUsers()).find((m) => { var _a; return !!((_a = m.invitation) === null || _a === void 0 ? void 0 : _a.tokenHash) && tokensMatch(m.invitation.tokenHash, tokenHash); })) !== null && _a !== void 0 ? _a : null);
|
|
1436
|
+
});
|
|
1437
|
+
}
|
|
1438
|
+
upsertPendingTenantUser(tenantId, userId, invitation) {
|
|
1439
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1440
|
+
const existing = yield this.findTenantUser(tenantId, userId);
|
|
1441
|
+
if (existing === null || existing === void 0 ? void 0 : existing.id) {
|
|
1442
|
+
yield this.entityStorage.updateOneAsEngine(TENANT_USER_ENTITY, existing.id, {
|
|
1443
|
+
statusId: core_2.AXPSystemStatusType.Pending,
|
|
1444
|
+
invitation,
|
|
1445
|
+
endDate: null,
|
|
1446
|
+
});
|
|
1447
|
+
return existing.id;
|
|
1448
|
+
}
|
|
1449
|
+
const id = (0, crypto_1.randomUUID)();
|
|
1450
|
+
yield this.entityStorage.insertOneAsEngine(TENANT_USER_ENTITY, {
|
|
1451
|
+
id,
|
|
1452
|
+
tenantId,
|
|
1453
|
+
userId,
|
|
1454
|
+
statusId: core_2.AXPSystemStatusType.Pending,
|
|
1455
|
+
startDate: new Date(),
|
|
1456
|
+
invitation,
|
|
1457
|
+
});
|
|
1458
|
+
return id;
|
|
1459
|
+
});
|
|
1460
|
+
}
|
|
1461
|
+
upsertPendingAppAccess(tenantId, tenantUserId, applicationIds) {
|
|
1462
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1463
|
+
if (!applicationIds.length) {
|
|
1464
|
+
return;
|
|
1465
|
+
}
|
|
1466
|
+
const existing = yield this.listAppAccess(tenantUserId);
|
|
1467
|
+
const byApp = new Map(existing.map((row) => [row.applicationId, row]));
|
|
1468
|
+
for (const applicationId of applicationIds) {
|
|
1469
|
+
const row = byApp.get(applicationId);
|
|
1470
|
+
if (row === null || row === void 0 ? void 0 : row.id) {
|
|
1471
|
+
yield this.entityStorage.updateOneAsEngine(APP_ACCESS_ENTITY, row.id, {
|
|
1472
|
+
statusId: core_2.AXPSystemStatusType.Pending,
|
|
1473
|
+
endDate: null,
|
|
1474
|
+
});
|
|
1475
|
+
}
|
|
1476
|
+
else {
|
|
1477
|
+
yield this.entityStorage.insertOneAsEngine(APP_ACCESS_ENTITY, {
|
|
1478
|
+
id: (0, crypto_1.randomUUID)(),
|
|
1479
|
+
tenantUserId,
|
|
1480
|
+
tenantId,
|
|
1481
|
+
applicationId,
|
|
1482
|
+
statusId: core_2.AXPSystemStatusType.Pending,
|
|
1483
|
+
startDate: new Date(),
|
|
1484
|
+
});
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
});
|
|
1488
|
+
}
|
|
1489
|
+
listAppAccess(tenantUserId) {
|
|
1490
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1491
|
+
const result = yield this.entityStorage.query(APP_ACCESS_ENTITY, (0, abstract_1.bypassAllEntityVisibilityFilters)({ skip: 0, take: 5000 }));
|
|
1492
|
+
const items = (0, lodash_1.get)(result, 'items', []);
|
|
1493
|
+
return (Array.isArray(items) ? items : []).filter((row) => row.tenantUserId === tenantUserId);
|
|
1494
|
+
});
|
|
1495
|
+
}
|
|
1496
|
+
/**
|
|
1497
|
+
* Terminal invite outcomes keep invitation audit metadata (no tokenHash).
|
|
1498
|
+
* Does not change User.accountState.
|
|
1499
|
+
*/
|
|
1500
|
+
resolveMembershipStatus(membership, statusId) {
|
|
1501
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1502
|
+
if (!membership.id) {
|
|
1503
|
+
return;
|
|
1504
|
+
}
|
|
1505
|
+
const invitation = membership.invitation
|
|
1506
|
+
? invitationWithoutToken(membership.invitation)
|
|
1507
|
+
: null;
|
|
1508
|
+
yield this.entityStorage.updateOneAsEngine(TENANT_USER_ENTITY, membership.id, {
|
|
1509
|
+
statusId,
|
|
1510
|
+
invitation,
|
|
1511
|
+
endDate: new Date(),
|
|
1512
|
+
});
|
|
1513
|
+
for (const row of yield this.listAppAccess(membership.id)) {
|
|
1514
|
+
if (row.id) {
|
|
1515
|
+
yield this.entityStorage.updateOneAsEngine(APP_ACCESS_ENTITY, row.id, {
|
|
1516
|
+
statusId,
|
|
1517
|
+
endDate: new Date(),
|
|
1518
|
+
});
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
});
|
|
1522
|
+
}
|
|
1523
|
+
resolveTenantTitle(tenantId) {
|
|
1524
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1525
|
+
try {
|
|
1526
|
+
const tenant = (yield this.entityStorage.getOneAsEngine(TENANT_ENTITY, tenantId));
|
|
1527
|
+
return this.extractTenantTitle(tenant === null || tenant === void 0 ? void 0 : tenant.title);
|
|
1528
|
+
}
|
|
1529
|
+
catch (_a) {
|
|
1530
|
+
return undefined;
|
|
1531
|
+
}
|
|
1532
|
+
});
|
|
1533
|
+
}
|
|
1534
|
+
maybeDeliverInvitationInApp(args) {
|
|
1535
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1536
|
+
if (!args.userAlreadyExists || args.user.accountState !== ACTIVE_ACCOUNT_STATE) {
|
|
1537
|
+
return;
|
|
1538
|
+
}
|
|
1539
|
+
try {
|
|
1540
|
+
yield this.deliverInvitationInApp(args);
|
|
1541
|
+
}
|
|
1542
|
+
catch (error) {
|
|
1543
|
+
this.logger.warn(`user-invitation in-app notify failed for user ${args.user.id}: ${error instanceof Error ? error.message : String(error)}`);
|
|
1544
|
+
}
|
|
1545
|
+
});
|
|
1546
|
+
}
|
|
1547
|
+
/**
|
|
1548
|
+
* Unscoped in-app row (empty scopeInfo) for existing active users — visible in any
|
|
1549
|
+
* tenant via catalog-read; inbox still filters by recipientUserId.
|
|
1550
|
+
*/
|
|
1551
|
+
deliverInvitationInApp(args) {
|
|
1552
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1553
|
+
var _a, _b;
|
|
1554
|
+
const recipientUserId = (_a = args.user.id) === null || _a === void 0 ? void 0 : _a.trim();
|
|
1555
|
+
if (!recipientUserId) {
|
|
1556
|
+
return;
|
|
1557
|
+
}
|
|
1558
|
+
const tenantTitle = (yield this.resolveTenantTitle(args.tenantId)) || args.tenantId;
|
|
1559
|
+
const correlationId = `membership-invitation:${args.tenantUserId}`;
|
|
1560
|
+
const result = yield this.commandExecutor.execute(module_communication_management_contracts_1.AXMCommunicationManagementServerCommands.Send, {
|
|
1561
|
+
title: '@security-management:users.notifications.membership-invitation.title',
|
|
1562
|
+
message: {
|
|
1563
|
+
subject: '@security-management:users.notifications.membership-invitation.title',
|
|
1564
|
+
body: tenantTitle,
|
|
1565
|
+
},
|
|
1566
|
+
channel: 'in-app',
|
|
1567
|
+
recipientUserId,
|
|
1568
|
+
recipientName: args.user.displayName || args.user.username,
|
|
1569
|
+
unscoped: true,
|
|
1570
|
+
definitionKey: module_security_management_contracts_1.AXM_MEMBERSHIP_INVITATION_NOTIFICATION_DEFINITION_KEY,
|
|
1571
|
+
correlationId,
|
|
1572
|
+
idempotencyKey: `${correlationId}:${Date.now()}`,
|
|
1573
|
+
priority: 'Notice',
|
|
1574
|
+
// Sender session: without this, debug mode redirects delivery to the admin.
|
|
1575
|
+
bypassDebugRedirect: true,
|
|
1576
|
+
actions: [
|
|
1577
|
+
{
|
|
1578
|
+
title: '@security-management:users.notifications.membership-invitation.actions.review',
|
|
1579
|
+
command: {
|
|
1580
|
+
name: module_auth_contracts_1.AXMAuthCommandKeys.Account.OpenWaitingRoom,
|
|
1581
|
+
options: {
|
|
1582
|
+
source: module_security_management_contracts_1.AXM_MEMBERSHIP_INVITE_WAITING_ROOM_SOURCE,
|
|
1583
|
+
},
|
|
1584
|
+
},
|
|
1585
|
+
},
|
|
1586
|
+
],
|
|
1587
|
+
});
|
|
1588
|
+
if (!(result === null || result === void 0 ? void 0 : result.success)) {
|
|
1589
|
+
const text = typeof ((_b = result === null || result === void 0 ? void 0 : result.message) === null || _b === void 0 ? void 0 : _b.text) === 'string'
|
|
1590
|
+
? result.message.text
|
|
1591
|
+
: 'Failed to send membership invitation in-app notification.';
|
|
1592
|
+
throw new Error(text);
|
|
1593
|
+
}
|
|
1594
|
+
});
|
|
1595
|
+
}
|
|
1596
|
+
deliverInvitationEmail(args) {
|
|
1597
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1598
|
+
var _a, _b, _c, _d;
|
|
1599
|
+
const platformTenantId = (_a = (0, abstract_1.getPrimaryPlatformTenantId)()) === null || _a === void 0 ? void 0 : _a.trim();
|
|
1600
|
+
if (!platformTenantId) {
|
|
1601
|
+
this.logger.warn(`user-invitation missing platform tenant id for user ${args.user.id}`);
|
|
1602
|
+
throw new Error('Platform tenant context is required to send email.');
|
|
1603
|
+
}
|
|
1604
|
+
const inviteLink = `${resolveWebOrigin(args.webOrigin)}/auth/accept-invitation?token=${encodeURIComponent(args.rawToken)}`;
|
|
1605
|
+
const merge = {
|
|
1606
|
+
userName: args.user.displayName || args.user.username || args.email,
|
|
1607
|
+
inviteLink,
|
|
1608
|
+
expiryDays: 7,
|
|
1609
|
+
expiryMinutes: EXPIRY_MINUTES,
|
|
1610
|
+
};
|
|
1611
|
+
const renderResult = yield this.commandExecutor.execute(module_content_management_contracts_1.AXM_TEXT_TEMPLATE_RENDER_COMMAND_KEY, {
|
|
1612
|
+
textTemplateId: TEMPLATE_ID,
|
|
1613
|
+
tenantId: platformTenantId,
|
|
1614
|
+
userId: args.user.id,
|
|
1615
|
+
channel: 'email',
|
|
1616
|
+
data: merge,
|
|
1617
|
+
});
|
|
1618
|
+
const rendered = (0, core_2.runtimeCommandPayloadData)(renderResult);
|
|
1619
|
+
if (!(renderResult === null || renderResult === void 0 ? void 0 : renderResult.success) || !(rendered === null || rendered === void 0 ? void 0 : rendered.body)) {
|
|
1620
|
+
const text = typeof ((_b = renderResult === null || renderResult === void 0 ? void 0 : renderResult.message) === null || _b === void 0 ? void 0 : _b.text) === 'string'
|
|
1621
|
+
? renderResult.message.text
|
|
1622
|
+
: 'User invitation template is not configured.';
|
|
1623
|
+
this.logger.warn(`user-invitation text template render failed: ${text}`);
|
|
1624
|
+
throw new Error(text);
|
|
1625
|
+
}
|
|
1626
|
+
const subject = rendered.subject;
|
|
1627
|
+
const body = rendered.body;
|
|
1628
|
+
const result = yield this.commandExecutor.execute(module_communication_management_contracts_1.AXMCommunicationManagementServerCommands.Send, {
|
|
1629
|
+
title: subject || 'You are invited',
|
|
1630
|
+
message: { subject, body },
|
|
1631
|
+
channel: EMAIL_CHANNEL,
|
|
1632
|
+
recipientAddress: args.email,
|
|
1633
|
+
recipientUserId: args.user.id,
|
|
1634
|
+
recipientName: args.user.displayName || args.user.username,
|
|
1635
|
+
tenantId: platformTenantId,
|
|
1636
|
+
definitionKey: TEMPLATE_ID,
|
|
1637
|
+
correlationId: `user-invitation:${args.user.id}:${args.invitation.sentAt}`,
|
|
1638
|
+
idempotencyKey: `user-invitation:${args.user.id}:${(_c = args.invitation.tokenHash) !== null && _c !== void 0 ? _c : args.invitation.sentAt}`,
|
|
1639
|
+
});
|
|
1640
|
+
if (!(result === null || result === void 0 ? void 0 : result.success)) {
|
|
1641
|
+
const text = typeof ((_d = result === null || result === void 0 ? void 0 : result.message) === null || _d === void 0 ? void 0 : _d.text) === 'string'
|
|
1642
|
+
? result.message.text
|
|
1643
|
+
: 'Failed to send invitation email.';
|
|
1644
|
+
this.logger.warn(`user-invitation send failed for user ${args.user.id}: ${text}`);
|
|
1645
|
+
throw new Error(text);
|
|
1646
|
+
}
|
|
1647
|
+
return true;
|
|
1648
|
+
});
|
|
1649
|
+
}
|
|
1650
|
+
};
|
|
1651
|
+
exports.UserInvitationService = UserInvitationService;
|
|
1652
|
+
exports.UserInvitationService = UserInvitationService = UserInvitationService_1 = tslib_1.__decorate([
|
|
1653
|
+
(0, common_1.Injectable)(),
|
|
1654
|
+
tslib_1.__metadata("design:paramtypes", [core_1.EntityStorageMiddlewareService,
|
|
1655
|
+
providers_1.ServerCommandExecutorService])
|
|
1656
|
+
], UserInvitationService);
|
|
1657
|
+
//#endregion
|
|
1658
|
+
//# sourceMappingURL=user-invitation.service.js.map
|