@portaidentity/server 1.7.3 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -0
- package/dist/applications/index.d.ts +2 -2
- package/dist/applications/index.d.ts.map +1 -1
- package/dist/applications/index.js +2 -2
- package/dist/applications/index.js.map +1 -1
- package/dist/applications/repository.d.ts +49 -4
- package/dist/applications/repository.d.ts.map +1 -1
- package/dist/applications/repository.js +148 -10
- package/dist/applications/repository.js.map +1 -1
- package/dist/applications/service.d.ts +35 -17
- package/dist/applications/service.d.ts.map +1 -1
- package/dist/applications/service.js +144 -35
- package/dist/applications/service.js.map +1 -1
- package/dist/applications/types.d.ts +1 -1
- package/dist/applications/types.d.ts.map +1 -1
- package/dist/applications/validators.d.ts +14 -0
- package/dist/applications/validators.d.ts.map +1 -0
- package/dist/applications/validators.js +14 -0
- package/dist/applications/validators.js.map +1 -0
- package/dist/auth/effective-branding.d.ts +35 -0
- package/dist/auth/effective-branding.d.ts.map +1 -0
- package/dist/auth/effective-branding.js +81 -0
- package/dist/auth/effective-branding.js.map +1 -0
- package/dist/auth/email-service.d.ts +3 -8
- package/dist/auth/email-service.d.ts.map +1 -1
- package/dist/auth/email-service.js +22 -30
- package/dist/auth/email-service.js.map +1 -1
- package/dist/auth/i18n.d.ts +5 -0
- package/dist/auth/i18n.d.ts.map +1 -1
- package/dist/auth/i18n.js +6 -5
- package/dist/auth/i18n.js.map +1 -1
- package/dist/auth/rate-limiter.d.ts +6 -10
- package/dist/auth/rate-limiter.d.ts.map +1 -1
- package/dist/auth/rate-limiter.js +12 -16
- package/dist/auth/rate-limiter.js.map +1 -1
- package/dist/auth/recovery-job-processor.d.ts.map +1 -1
- package/dist/auth/recovery-job-processor.js +1 -1
- package/dist/auth/recovery-job-processor.js.map +1 -1
- package/dist/auth/template-engine.d.ts +3 -1
- package/dist/auth/template-engine.d.ts.map +1 -1
- package/dist/auth/template-engine.js +1 -1
- package/dist/auth/template-engine.js.map +1 -1
- package/dist/cli/commands/init.d.ts +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +12 -12
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/error-handler.d.ts +3 -2
- package/dist/cli/error-handler.d.ts.map +1 -1
- package/dist/cli/error-handler.js +9 -2
- package/dist/cli/error-handler.js.map +1 -1
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/clients/index.d.ts +1 -1
- package/dist/clients/index.js +1 -1
- package/dist/clients/repository.d.ts +18 -0
- package/dist/clients/repository.d.ts.map +1 -1
- package/dist/clients/repository.js +36 -0
- package/dist/clients/repository.js.map +1 -1
- package/dist/clients/secret-repository.d.ts +35 -8
- package/dist/clients/secret-repository.d.ts.map +1 -1
- package/dist/clients/secret-repository.js +69 -16
- package/dist/clients/secret-repository.js.map +1 -1
- package/dist/clients/secret-service.d.ts +5 -9
- package/dist/clients/secret-service.d.ts.map +1 -1
- package/dist/clients/secret-service.js +13 -22
- package/dist/clients/secret-service.js.map +1 -1
- package/dist/clients/service.d.ts +10 -13
- package/dist/clients/service.d.ts.map +1 -1
- package/dist/clients/service.js +112 -40
- package/dist/clients/service.js.map +1 -1
- package/dist/clients/types.d.ts +1 -1
- package/dist/clients/types.d.ts.map +1 -1
- package/dist/clients/validators.d.ts +83 -1
- package/dist/clients/validators.d.ts.map +1 -1
- package/dist/clients/validators.js +110 -0
- package/dist/clients/validators.js.map +1 -1
- package/dist/config/index.d.ts +1 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +1 -0
- package/dist/config/index.js.map +1 -1
- package/dist/config/schema.d.ts +1 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +23 -1
- package/dist/config/schema.js.map +1 -1
- package/dist/custom-claims/repository.d.ts +14 -9
- package/dist/custom-claims/repository.d.ts.map +1 -1
- package/dist/custom-claims/repository.js +38 -11
- package/dist/custom-claims/repository.js.map +1 -1
- package/dist/custom-claims/service.d.ts +11 -6
- package/dist/custom-claims/service.d.ts.map +1 -1
- package/dist/custom-claims/service.js +43 -20
- package/dist/custom-claims/service.js.map +1 -1
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/lib/admin-permissions.d.ts +12 -7
- package/dist/lib/admin-permissions.d.ts.map +1 -1
- package/dist/lib/admin-permissions.js +26 -16
- package/dist/lib/admin-permissions.js.map +1 -1
- package/dist/lib/audit-log.d.ts +2 -2
- package/dist/lib/audit-log.d.ts.map +1 -1
- package/dist/lib/audit-log.js +1 -1
- package/dist/lib/audit-log.js.map +1 -1
- package/dist/lib/authority-revocation.d.ts +18 -0
- package/dist/lib/authority-revocation.d.ts.map +1 -0
- package/dist/lib/authority-revocation.js +37 -0
- package/dist/lib/authority-revocation.js.map +1 -0
- package/dist/lib/branding-assets.d.ts +35 -5
- package/dist/lib/branding-assets.d.ts.map +1 -1
- package/dist/lib/branding-assets.js +33 -26
- package/dist/lib/branding-assets.js.map +1 -1
- package/dist/lib/bulk-operations.d.ts +1 -1
- package/dist/lib/bulk-operations.d.ts.map +1 -1
- package/dist/lib/bulk-operations.js +1 -13
- package/dist/lib/bulk-operations.js.map +1 -1
- package/dist/lib/cursor.d.ts +2 -0
- package/dist/lib/cursor.d.ts.map +1 -1
- package/dist/lib/cursor.js +2 -7
- package/dist/lib/cursor.js.map +1 -1
- package/dist/lib/database.d.ts.map +1 -1
- package/dist/lib/database.js +5 -0
- package/dist/lib/database.js.map +1 -1
- package/dist/lib/deletion-cleanup.d.ts +43 -0
- package/dist/lib/deletion-cleanup.d.ts.map +1 -0
- package/dist/lib/deletion-cleanup.js +207 -0
- package/dist/lib/deletion-cleanup.js.map +1 -0
- package/dist/lib/image-validator.d.ts +3 -1
- package/dist/lib/image-validator.d.ts.map +1 -1
- package/dist/lib/image-validator.js +32 -19
- package/dist/lib/image-validator.js.map +1 -1
- package/dist/lib/log-format.d.ts +20 -0
- package/dist/lib/log-format.d.ts.map +1 -0
- package/dist/lib/log-format.js +22 -0
- package/dist/lib/log-format.js.map +1 -0
- package/dist/lib/logger.d.ts +3 -1
- package/dist/lib/logger.d.ts.map +1 -1
- package/dist/lib/logger.js +21 -4
- package/dist/lib/logger.js.map +1 -1
- package/dist/lib/redis.d.ts.map +1 -1
- package/dist/lib/redis.js +3 -0
- package/dist/lib/redis.js.map +1 -1
- package/dist/lib/session-tracking.d.ts +73 -11
- package/dist/lib/session-tracking.d.ts.map +1 -1
- package/dist/lib/session-tracking.js +76 -32
- package/dist/lib/session-tracking.js.map +1 -1
- package/dist/lib/signing-key-crypto.d.ts.map +1 -1
- package/dist/lib/signing-key-crypto.js +4 -5
- package/dist/lib/signing-key-crypto.js.map +1 -1
- package/dist/lib/signing-keys.d.ts +3 -4
- package/dist/lib/signing-keys.d.ts.map +1 -1
- package/dist/lib/signing-keys.js +66 -41
- package/dist/lib/signing-keys.js.map +1 -1
- package/dist/lib/super-admin-protection.d.ts +5 -5
- package/dist/lib/super-admin-protection.d.ts.map +1 -1
- package/dist/lib/super-admin-protection.js +6 -9
- package/dist/lib/super-admin-protection.js.map +1 -1
- package/dist/lib/system-config-catalog.d.ts +268 -0
- package/dist/lib/system-config-catalog.d.ts.map +1 -0
- package/dist/lib/system-config-catalog.js +90 -0
- package/dist/lib/system-config-catalog.js.map +1 -0
- package/dist/lib/system-config.d.ts +30 -33
- package/dist/lib/system-config.d.ts.map +1 -1
- package/dist/lib/system-config.js +78 -93
- package/dist/lib/system-config.js.map +1 -1
- package/dist/lib/with-timeout.d.ts +32 -0
- package/dist/lib/with-timeout.d.ts.map +1 -0
- package/dist/lib/with-timeout.js +45 -0
- package/dist/lib/with-timeout.js.map +1 -0
- package/dist/middleware/admin-auth.d.ts +6 -5
- package/dist/middleware/admin-auth.d.ts.map +1 -1
- package/dist/middleware/admin-auth.js +30 -20
- package/dist/middleware/admin-auth.js.map +1 -1
- package/dist/middleware/admin-cors.d.ts +3 -3
- package/dist/middleware/admin-cors.js +3 -3
- package/dist/middleware/admin-mutation-audit.d.ts +3 -2
- package/dist/middleware/admin-mutation-audit.d.ts.map +1 -1
- package/dist/middleware/admin-mutation-audit.js +12 -5
- package/dist/middleware/admin-mutation-audit.js.map +1 -1
- package/dist/middleware/admin-rate-limiter.d.ts +7 -8
- package/dist/middleware/admin-rate-limiter.d.ts.map +1 -1
- package/dist/middleware/admin-rate-limiter.js +7 -8
- package/dist/middleware/admin-rate-limiter.js.map +1 -1
- package/dist/middleware/client-secret-hash.d.ts +10 -29
- package/dist/middleware/client-secret-hash.d.ts.map +1 -1
- package/dist/middleware/client-secret-hash.js +161 -59
- package/dist/middleware/client-secret-hash.js.map +1 -1
- package/dist/middleware/health.d.ts +17 -0
- package/dist/middleware/health.d.ts.map +1 -1
- package/dist/middleware/health.js +25 -21
- package/dist/middleware/health.js.map +1 -1
- package/dist/middleware/ready.d.ts.map +1 -1
- package/dist/middleware/ready.js +1 -12
- package/dist/middleware/ready.js.map +1 -1
- package/dist/middleware/require-existing-organization.d.ts +18 -0
- package/dist/middleware/require-existing-organization.d.ts.map +1 -0
- package/dist/middleware/require-existing-organization.js +38 -0
- package/dist/middleware/require-existing-organization.js.map +1 -0
- package/dist/middleware/require-permission.d.ts.map +1 -1
- package/dist/middleware/require-permission.js +35 -1
- package/dist/middleware/require-permission.js.map +1 -1
- package/dist/middleware/require-user-organization.d.ts.map +1 -1
- package/dist/middleware/require-user-organization.js +4 -2
- package/dist/middleware/require-user-organization.js.map +1 -1
- package/dist/middleware/security-headers.d.ts +18 -0
- package/dist/middleware/security-headers.d.ts.map +1 -1
- package/dist/middleware/security-headers.js +32 -1
- package/dist/middleware/security-headers.js.map +1 -1
- package/dist/middleware/tenant-resolver.d.ts +1 -2
- package/dist/middleware/tenant-resolver.d.ts.map +1 -1
- package/dist/middleware/tenant-resolver.js +1 -5
- package/dist/middleware/tenant-resolver.js.map +1 -1
- package/dist/middleware/token-rate-limiter.d.ts +37 -16
- package/dist/middleware/token-rate-limiter.d.ts.map +1 -1
- package/dist/middleware/token-rate-limiter.js +126 -54
- package/dist/middleware/token-rate-limiter.js.map +1 -1
- package/dist/oidc/account-finder.d.ts +5 -5
- package/dist/oidc/account-finder.d.ts.map +1 -1
- package/dist/oidc/account-finder.js +31 -23
- package/dist/oidc/account-finder.js.map +1 -1
- package/dist/oidc/adapter-factory.d.ts.map +1 -1
- package/dist/oidc/adapter-factory.js +83 -3
- package/dist/oidc/adapter-factory.js.map +1 -1
- package/dist/oidc/configuration.d.ts +3 -8
- package/dist/oidc/configuration.d.ts.map +1 -1
- package/dist/oidc/configuration.js +64 -50
- package/dist/oidc/configuration.js.map +1 -1
- package/dist/oidc/provider.d.ts.map +1 -1
- package/dist/oidc/provider.js +7 -0
- package/dist/oidc/provider.js.map +1 -1
- package/dist/oidc/redis-adapter.d.ts +2 -3
- package/dist/oidc/redis-adapter.d.ts.map +1 -1
- package/dist/oidc/redis-adapter.js +54 -46
- package/dist/oidc/redis-adapter.js.map +1 -1
- package/dist/organizations/branding-url.d.ts +17 -0
- package/dist/organizations/branding-url.d.ts.map +1 -0
- package/dist/organizations/branding-url.js +38 -0
- package/dist/organizations/branding-url.js.map +1 -0
- package/dist/organizations/index.d.ts +2 -2
- package/dist/organizations/index.d.ts.map +1 -1
- package/dist/organizations/index.js +1 -1
- package/dist/organizations/index.js.map +1 -1
- package/dist/organizations/repository.d.ts +22 -32
- package/dist/organizations/repository.d.ts.map +1 -1
- package/dist/organizations/repository.js +64 -45
- package/dist/organizations/repository.js.map +1 -1
- package/dist/organizations/service.d.ts +14 -46
- package/dist/organizations/service.d.ts.map +1 -1
- package/dist/organizations/service.js +60 -112
- package/dist/organizations/service.js.map +1 -1
- package/dist/organizations/types.d.ts +1 -15
- package/dist/organizations/types.d.ts.map +1 -1
- package/dist/organizations/validators.d.ts +41 -0
- package/dist/organizations/validators.d.ts.map +1 -0
- package/dist/organizations/validators.js +48 -0
- package/dist/organizations/validators.js.map +1 -0
- package/dist/portability/apply.d.ts +15 -0
- package/dist/portability/apply.d.ts.map +1 -0
- package/dist/portability/apply.js +274 -0
- package/dist/portability/apply.js.map +1 -0
- package/dist/portability/authorization.d.ts +30 -0
- package/dist/portability/authorization.d.ts.map +1 -0
- package/dist/portability/authorization.js +130 -0
- package/dist/portability/authorization.js.map +1 -0
- package/dist/portability/cleanup.d.ts +29 -0
- package/dist/portability/cleanup.d.ts.map +1 -0
- package/dist/portability/cleanup.js +167 -0
- package/dist/portability/cleanup.js.map +1 -0
- package/dist/portability/export.d.ts +18 -0
- package/dist/portability/export.d.ts.map +1 -0
- package/dist/portability/export.js +125 -0
- package/dist/portability/export.js.map +1 -0
- package/dist/portability/import-repository.d.ts +204 -0
- package/dist/portability/import-repository.d.ts.map +1 -0
- package/dist/portability/import-repository.js +347 -0
- package/dist/portability/import-repository.js.map +1 -0
- package/dist/portability/import-user-client-writers.d.ts +40 -0
- package/dist/portability/import-user-client-writers.d.ts.map +1 -0
- package/dist/portability/import-user-client-writers.js +168 -0
- package/dist/portability/import-user-client-writers.js.map +1 -0
- package/dist/portability/index.d.ts +8 -0
- package/dist/portability/index.d.ts.map +1 -0
- package/dist/portability/index.js +8 -0
- package/dist/portability/index.js.map +1 -0
- package/dist/portability/plan-support.d.ts +137 -0
- package/dist/portability/plan-support.d.ts.map +1 -0
- package/dist/portability/plan-support.js +336 -0
- package/dist/portability/plan-support.js.map +1 -0
- package/dist/portability/plan.d.ts +34 -0
- package/dist/portability/plan.d.ts.map +1 -0
- package/dist/portability/plan.js +450 -0
- package/dist/portability/plan.js.map +1 -0
- package/dist/portability/repository.d.ts +95 -0
- package/dist/portability/repository.d.ts.map +1 -0
- package/dist/portability/repository.js +388 -0
- package/dist/portability/repository.js.map +1 -0
- package/dist/portability/schema.d.ts +589 -0
- package/dist/portability/schema.d.ts.map +1 -0
- package/dist/portability/schema.js +458 -0
- package/dist/portability/schema.js.map +1 -0
- package/dist/portability/types.d.ts +381 -0
- package/dist/portability/types.d.ts.map +1 -0
- package/dist/portability/types.js +19 -0
- package/dist/portability/types.js.map +1 -0
- package/dist/rbac/errors.d.ts +4 -0
- package/dist/rbac/errors.d.ts.map +1 -1
- package/dist/rbac/errors.js +7 -0
- package/dist/rbac/errors.js.map +1 -1
- package/dist/rbac/index.d.ts +3 -3
- package/dist/rbac/index.d.ts.map +1 -1
- package/dist/rbac/index.js +3 -3
- package/dist/rbac/index.js.map +1 -1
- package/dist/rbac/mapping-repository.d.ts +84 -9
- package/dist/rbac/mapping-repository.d.ts.map +1 -1
- package/dist/rbac/mapping-repository.js +222 -55
- package/dist/rbac/mapping-repository.js.map +1 -1
- package/dist/rbac/permission-repository.d.ts +36 -19
- package/dist/rbac/permission-repository.d.ts.map +1 -1
- package/dist/rbac/permission-repository.js +93 -35
- package/dist/rbac/permission-repository.js.map +1 -1
- package/dist/rbac/permission-service.d.ts +18 -19
- package/dist/rbac/permission-service.d.ts.map +1 -1
- package/dist/rbac/permission-service.js +100 -57
- package/dist/rbac/permission-service.js.map +1 -1
- package/dist/rbac/role-repository.d.ts +29 -24
- package/dist/rbac/role-repository.d.ts.map +1 -1
- package/dist/rbac/role-repository.js +66 -43
- package/dist/rbac/role-repository.js.map +1 -1
- package/dist/rbac/role-service.d.ts +35 -32
- package/dist/rbac/role-service.d.ts.map +1 -1
- package/dist/rbac/role-service.js +210 -96
- package/dist/rbac/role-service.js.map +1 -1
- package/dist/rbac/slugs.d.ts +16 -27
- package/dist/rbac/slugs.d.ts.map +1 -1
- package/dist/rbac/slugs.js +36 -59
- package/dist/rbac/slugs.js.map +1 -1
- package/dist/rbac/types.d.ts +2 -4
- package/dist/rbac/types.d.ts.map +1 -1
- package/dist/rbac/types.js.map +1 -1
- package/dist/rbac/user-role-service.d.ts +23 -24
- package/dist/rbac/user-role-service.d.ts.map +1 -1
- package/dist/rbac/user-role-service.js +117 -61
- package/dist/rbac/user-role-service.js.map +1 -1
- package/dist/routes/applications.d.ts +4 -2
- package/dist/routes/applications.d.ts.map +1 -1
- package/dist/routes/applications.js +57 -19
- package/dist/routes/applications.js.map +1 -1
- package/dist/routes/audit.js +1 -1
- package/dist/routes/audit.js.map +1 -1
- package/dist/routes/branding.d.ts +3 -3
- package/dist/routes/branding.d.ts.map +1 -1
- package/dist/routes/branding.js +44 -16
- package/dist/routes/branding.js.map +1 -1
- package/dist/routes/bulk.d.ts +0 -5
- package/dist/routes/bulk.d.ts.map +1 -1
- package/dist/routes/bulk.js +3 -5
- package/dist/routes/bulk.js.map +1 -1
- package/dist/routes/clients.d.ts +4 -4
- package/dist/routes/clients.d.ts.map +1 -1
- package/dist/routes/clients.js +111 -52
- package/dist/routes/clients.js.map +1 -1
- package/dist/routes/config.d.ts +17 -12
- package/dist/routes/config.d.ts.map +1 -1
- package/dist/routes/config.js +165 -64
- package/dist/routes/config.js.map +1 -1
- package/dist/routes/custom-claims.d.ts.map +1 -1
- package/dist/routes/custom-claims.js +8 -2
- package/dist/routes/custom-claims.js.map +1 -1
- package/dist/routes/exports.d.ts +2 -1
- package/dist/routes/exports.d.ts.map +1 -1
- package/dist/routes/exports.js +57 -1
- package/dist/routes/exports.js.map +1 -1
- package/dist/routes/imports.d.ts +2 -2
- package/dist/routes/imports.d.ts.map +1 -1
- package/dist/routes/imports.js +95 -40
- package/dist/routes/imports.js.map +1 -1
- package/dist/routes/interactions.d.ts.map +1 -1
- package/dist/routes/interactions.js +16 -34
- package/dist/routes/interactions.js.map +1 -1
- package/dist/routes/invitation.d.ts.map +1 -1
- package/dist/routes/invitation.js +10 -23
- package/dist/routes/invitation.js.map +1 -1
- package/dist/routes/keys.d.ts.map +1 -1
- package/dist/routes/keys.js +26 -13
- package/dist/routes/keys.js.map +1 -1
- package/dist/routes/magic-link.d.ts.map +1 -1
- package/dist/routes/magic-link.js +5 -17
- package/dist/routes/magic-link.js.map +1 -1
- package/dist/routes/organizations.d.ts +1 -2
- package/dist/routes/organizations.d.ts.map +1 -1
- package/dist/routes/organizations.js +43 -104
- package/dist/routes/organizations.js.map +1 -1
- package/dist/routes/password-reset.d.ts.map +1 -1
- package/dist/routes/password-reset.js +13 -35
- package/dist/routes/password-reset.js.map +1 -1
- package/dist/routes/permissions.d.ts +1 -1
- package/dist/routes/permissions.d.ts.map +1 -1
- package/dist/routes/permissions.js +58 -17
- package/dist/routes/permissions.js.map +1 -1
- package/dist/routes/public-branding.d.ts +16 -0
- package/dist/routes/public-branding.d.ts.map +1 -0
- package/dist/routes/public-branding.js +52 -0
- package/dist/routes/public-branding.js.map +1 -0
- package/dist/routes/roles.d.ts +1 -1
- package/dist/routes/roles.d.ts.map +1 -1
- package/dist/routes/roles.js +63 -21
- package/dist/routes/roles.js.map +1 -1
- package/dist/routes/sessions.d.ts.map +1 -1
- package/dist/routes/sessions.js +23 -12
- package/dist/routes/sessions.js.map +1 -1
- package/dist/routes/two-factor.d.ts.map +1 -1
- package/dist/routes/two-factor.js +91 -38
- package/dist/routes/two-factor.js.map +1 -1
- package/dist/routes/user-roles.d.ts.map +1 -1
- package/dist/routes/user-roles.js +56 -9
- package/dist/routes/user-roles.js.map +1 -1
- package/dist/routes/users.d.ts +4 -8
- package/dist/routes/users.d.ts.map +1 -1
- package/dist/routes/users.js +108 -265
- package/dist/routes/users.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +53 -37
- package/dist/server.js.map +1 -1
- package/dist/two-factor/errors.d.ts +9 -0
- package/dist/two-factor/errors.d.ts.map +1 -1
- package/dist/two-factor/errors.js +12 -0
- package/dist/two-factor/errors.js.map +1 -1
- package/dist/two-factor/index.d.ts +5 -4
- package/dist/two-factor/index.d.ts.map +1 -1
- package/dist/two-factor/index.js +4 -4
- package/dist/two-factor/index.js.map +1 -1
- package/dist/two-factor/repository.d.ts +17 -6
- package/dist/two-factor/repository.d.ts.map +1 -1
- package/dist/two-factor/repository.js +37 -9
- package/dist/two-factor/repository.js.map +1 -1
- package/dist/two-factor/service.d.ts.map +1 -1
- package/dist/two-factor/service.js +26 -19
- package/dist/two-factor/service.js.map +1 -1
- package/dist/two-factor/totp.d.ts +13 -2
- package/dist/two-factor/totp.d.ts.map +1 -1
- package/dist/two-factor/totp.js +20 -7
- package/dist/two-factor/totp.js.map +1 -1
- package/dist/two-factor/types.d.ts +3 -0
- package/dist/two-factor/types.d.ts.map +1 -1
- package/dist/two-factor/types.js +5 -0
- package/dist/two-factor/types.js.map +1 -1
- package/dist/users/gdpr.d.ts +2 -40
- package/dist/users/gdpr.d.ts.map +1 -1
- package/dist/users/gdpr.js +3 -110
- package/dist/users/gdpr.js.map +1 -1
- package/dist/users/index.d.ts +5 -4
- package/dist/users/index.d.ts.map +1 -1
- package/dist/users/index.js +5 -4
- package/dist/users/index.js.map +1 -1
- package/dist/users/repository.d.ts +49 -3
- package/dist/users/repository.d.ts.map +1 -1
- package/dist/users/repository.js +169 -18
- package/dist/users/repository.js.map +1 -1
- package/dist/users/service.d.ts +19 -51
- package/dist/users/service.d.ts.map +1 -1
- package/dist/users/service.js +72 -122
- package/dist/users/service.js.map +1 -1
- package/dist/users/types.d.ts +4 -5
- package/dist/users/types.d.ts.map +1 -1
- package/dist/users/types.js.map +1 -1
- package/dist/users/validators.d.ts +28 -0
- package/dist/users/validators.d.ts.map +1 -0
- package/dist/users/validators.js +25 -0
- package/dist/users/validators.js.map +1 -0
- package/locales/default/en/errors.json +2 -1
- package/migrations/024_application_client_correction.sql +39 -0
- package/migrations/025_record_deletion_lifecycle.sql +26 -0
- package/migrations/026_simplify_user_lifecycle.sql +33 -0
- package/migrations/027_branding_asset_size_limits.sql +19 -0
- package/migrations/028_totp_replay_protection.sql +17 -0
- package/migrations/029_public_session_identifiers.sql +16 -0
- package/migrations/030_global_configuration_catalog.sql +39 -0
- package/migrations/031_monotonic_updated_at.sql +28 -0
- package/package.json +2 -2
- package/dist/lib/data-import-plan.d.ts +0 -50
- package/dist/lib/data-import-plan.d.ts.map +0 -1
- package/dist/lib/data-import-plan.js +0 -235
- package/dist/lib/data-import-plan.js.map +0 -1
- package/dist/lib/data-import.d.ts +0 -276
- package/dist/lib/data-import.d.ts.map +0 -1
- package/dist/lib/data-import.js +0 -1694
- package/dist/lib/data-import.js.map +0 -1
package/dist/routes/users.js
CHANGED
|
@@ -12,16 +12,12 @@
|
|
|
12
12
|
* GET /:userId — Get user by ID
|
|
13
13
|
* PUT /:userId — Update user profile
|
|
14
14
|
* POST /:userId/deactivate — Deactivate (active → inactive)
|
|
15
|
-
* POST /:userId/
|
|
16
|
-
* POST /:userId/suspend — Suspend (active → suspended)
|
|
17
|
-
* POST /:userId/unsuspend — Unsuspend (suspended → active)
|
|
18
|
-
* POST /:userId/lock — Lock (active → locked)
|
|
19
|
-
* POST /:userId/unlock — Unlock (locked → active)
|
|
15
|
+
* POST /:userId/activate — Activate (inactive → active)
|
|
20
16
|
* POST /:userId/password — Set/change password
|
|
21
17
|
* DELETE /:userId/password — Clear password (passwordless)
|
|
22
18
|
* POST /:userId/verify-email — Mark email as verified
|
|
23
19
|
* GET /:userId/export — GDPR data export (Article 20)
|
|
24
|
-
*
|
|
20
|
+
* DELETE /:userId — Delete user
|
|
25
21
|
*
|
|
26
22
|
* Error mapping:
|
|
27
23
|
* UserNotFoundError → 404
|
|
@@ -32,7 +28,7 @@
|
|
|
32
28
|
import Router from '@koa/router';
|
|
33
29
|
import { z } from 'zod';
|
|
34
30
|
import { renderInvitationEmail, sendInvitationEmail } from '../auth/email-service.js';
|
|
35
|
-
import { insertInvitationToken
|
|
31
|
+
import { insertInvitationToken } from '../auth/token-repository.js';
|
|
36
32
|
import { generateToken } from '../auth/tokens.js';
|
|
37
33
|
import { config } from '../config/index.js';
|
|
38
34
|
import { ADMIN_PERMISSIONS } from '../lib/admin-permissions.js';
|
|
@@ -41,83 +37,83 @@ import { afterDatabaseCommit, getPool } from '../lib/database.js';
|
|
|
41
37
|
import { getEntityHistory } from '../lib/entity-history.js';
|
|
42
38
|
import { checkIfMatch, setETagHeader } from '../lib/etag.js';
|
|
43
39
|
import { guardSuperAdmin, SuperAdminProtectionError } from '../lib/super-admin-protection.js';
|
|
40
|
+
import { getSystemConfigNumber } from '../lib/system-config.js';
|
|
44
41
|
import { requireAdminAuth } from '../middleware/admin-auth.js';
|
|
45
42
|
import { requirePermission } from '../middleware/require-permission.js';
|
|
46
43
|
import { requireUserOrganization } from '../middleware/require-user-organization.js';
|
|
44
|
+
import { requireExistingOrganization } from '../middleware/require-existing-organization.js';
|
|
47
45
|
import { getOrganizationById } from '../organizations/service.js';
|
|
48
46
|
import { UserNotFoundError, UserValidationError } from '../users/errors.js';
|
|
49
|
-
import { exportUserData
|
|
47
|
+
import { exportUserData } from '../users/gdpr.js';
|
|
50
48
|
import * as userService from '../users/service.js';
|
|
49
|
+
import { userBirthdateSchema, userCountrySchema, userEmailSchema, userGenderSchema, userLocaleSchema, userPhoneNumberSchema, userPostalCodeSchema, userProfileNameSchema, userProfileUrlSchema, userZoneinfoSchema, } from '../users/validators.js';
|
|
51
50
|
// ---------------------------------------------------------------------------
|
|
52
51
|
// Validation schemas
|
|
53
52
|
// ---------------------------------------------------------------------------
|
|
54
53
|
/** Schema for creating a new user */
|
|
55
54
|
const createUserSchema = z.object({
|
|
56
|
-
email:
|
|
55
|
+
email: userEmailSchema,
|
|
57
56
|
password: z.string().min(8).max(128).optional(),
|
|
58
|
-
givenName:
|
|
59
|
-
familyName:
|
|
60
|
-
middleName:
|
|
61
|
-
nickname:
|
|
62
|
-
preferredUsername:
|
|
63
|
-
profileUrl:
|
|
64
|
-
pictureUrl:
|
|
65
|
-
websiteUrl:
|
|
66
|
-
gender:
|
|
67
|
-
birthdate:
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
zoneinfo: z.string().max(50).optional(),
|
|
72
|
-
locale: z.string().max(10).optional(),
|
|
73
|
-
phoneNumber: z.string().max(50).optional(),
|
|
57
|
+
givenName: userProfileNameSchema.optional(),
|
|
58
|
+
familyName: userProfileNameSchema.optional(),
|
|
59
|
+
middleName: userProfileNameSchema.optional(),
|
|
60
|
+
nickname: userProfileNameSchema.optional(),
|
|
61
|
+
preferredUsername: userProfileNameSchema.optional(),
|
|
62
|
+
profileUrl: userProfileUrlSchema.optional(),
|
|
63
|
+
pictureUrl: userProfileUrlSchema.optional(),
|
|
64
|
+
websiteUrl: userProfileUrlSchema.optional(),
|
|
65
|
+
gender: userGenderSchema.optional(),
|
|
66
|
+
birthdate: userBirthdateSchema.optional(),
|
|
67
|
+
zoneinfo: userZoneinfoSchema.optional(),
|
|
68
|
+
locale: userLocaleSchema.optional(),
|
|
69
|
+
phoneNumber: userPhoneNumberSchema.optional(),
|
|
74
70
|
phoneNumberVerified: z.boolean().optional(),
|
|
75
71
|
address: z
|
|
76
72
|
.object({
|
|
77
73
|
street: z.string().nullable().optional(),
|
|
78
|
-
locality:
|
|
79
|
-
region:
|
|
80
|
-
postalCode:
|
|
81
|
-
country:
|
|
74
|
+
locality: userProfileNameSchema.nullable().optional(),
|
|
75
|
+
region: userProfileNameSchema.nullable().optional(),
|
|
76
|
+
postalCode: userPostalCodeSchema.nullable().optional(),
|
|
77
|
+
country: userCountrySchema.nullable().optional(),
|
|
82
78
|
})
|
|
83
79
|
.optional(),
|
|
84
80
|
});
|
|
85
81
|
/** Schema for updating a user (all fields optional, nullable for clearing) */
|
|
86
|
-
const updateUserSchema = z
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
zoneinfo: z.string().max(50).nullable().optional(),
|
|
102
|
-
locale: z.string().max(10).nullable().optional(),
|
|
103
|
-
phoneNumber: z.string().max(50).nullable().optional(),
|
|
82
|
+
const updateUserSchema = z
|
|
83
|
+
.object({
|
|
84
|
+
givenName: userProfileNameSchema.nullable().optional(),
|
|
85
|
+
familyName: userProfileNameSchema.nullable().optional(),
|
|
86
|
+
middleName: userProfileNameSchema.nullable().optional(),
|
|
87
|
+
nickname: userProfileNameSchema.nullable().optional(),
|
|
88
|
+
preferredUsername: userProfileNameSchema.nullable().optional(),
|
|
89
|
+
profileUrl: userProfileUrlSchema.nullable().optional(),
|
|
90
|
+
pictureUrl: userProfileUrlSchema.nullable().optional(),
|
|
91
|
+
websiteUrl: userProfileUrlSchema.nullable().optional(),
|
|
92
|
+
gender: userGenderSchema.nullable().optional(),
|
|
93
|
+
birthdate: userBirthdateSchema.nullable().optional(),
|
|
94
|
+
zoneinfo: userZoneinfoSchema.nullable().optional(),
|
|
95
|
+
locale: userLocaleSchema.nullable().optional(),
|
|
96
|
+
phoneNumber: userPhoneNumberSchema.nullable().optional(),
|
|
104
97
|
phoneNumberVerified: z.boolean().optional(),
|
|
105
98
|
address: z
|
|
106
99
|
.object({
|
|
107
100
|
street: z.string().nullable().optional(),
|
|
108
|
-
locality:
|
|
109
|
-
region:
|
|
110
|
-
postalCode:
|
|
111
|
-
country:
|
|
101
|
+
locality: userProfileNameSchema.nullable().optional(),
|
|
102
|
+
region: userProfileNameSchema.nullable().optional(),
|
|
103
|
+
postalCode: userPostalCodeSchema.nullable().optional(),
|
|
104
|
+
country: userCountrySchema.nullable().optional(),
|
|
112
105
|
})
|
|
113
106
|
.nullable()
|
|
114
107
|
.optional(),
|
|
108
|
+
})
|
|
109
|
+
.refine((value) => Object.keys(value).length > 0, {
|
|
110
|
+
message: 'At least one profile field must be provided',
|
|
115
111
|
});
|
|
116
112
|
/** Schema for listing users with pagination */
|
|
117
113
|
const listUsersSchema = z.object({
|
|
118
114
|
page: z.coerce.number().int().min(1).default(1),
|
|
119
115
|
pageSize: z.coerce.number().int().min(1).max(100).default(20),
|
|
120
|
-
status: z.enum(['active', 'inactive', '
|
|
116
|
+
status: z.enum(['active', 'inactive', 'locked']).optional(),
|
|
121
117
|
search: z.string().max(255).optional(),
|
|
122
118
|
sortBy: z
|
|
123
119
|
.enum(['email', 'given_name', 'family_name', 'created_at', 'last_login_at'])
|
|
@@ -128,7 +124,7 @@ const listUsersSchema = z.object({
|
|
|
128
124
|
const listUsersCursorSchema = z.object({
|
|
129
125
|
cursor: z.string().optional(),
|
|
130
126
|
limit: z.coerce.number().int().min(1).max(100).default(25),
|
|
131
|
-
status: z.enum(['active', 'inactive', '
|
|
127
|
+
status: z.enum(['active', 'inactive', 'locked']).optional(),
|
|
132
128
|
search: z.string().max(255).optional(),
|
|
133
129
|
sortBy: z
|
|
134
130
|
.enum(['email', 'given_name', 'family_name', 'created_at', 'last_login_at'])
|
|
@@ -139,13 +135,10 @@ const listUsersCursorSchema = z.object({
|
|
|
139
135
|
const setPasswordSchema = z.object({
|
|
140
136
|
password: z.string().min(8).max(128),
|
|
141
137
|
});
|
|
142
|
-
/**
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
/** Schema for suspending a user (reason optional) */
|
|
147
|
-
const suspendUserSchema = z.object({
|
|
148
|
-
reason: z.string().max(500).optional(),
|
|
138
|
+
/** Organization-qualified parameters accepted by user deletion. */
|
|
139
|
+
const identifierSchema = z.object({
|
|
140
|
+
orgId: z.string().uuid(),
|
|
141
|
+
userId: z.string().uuid(),
|
|
149
142
|
});
|
|
150
143
|
// ---------------------------------------------------------------------------
|
|
151
144
|
// Error handler helper
|
|
@@ -181,8 +174,8 @@ function handleError(ctx, err) {
|
|
|
181
174
|
*
|
|
182
175
|
* All routes require admin authorization with granular permissions.
|
|
183
176
|
* Users are always scoped to an organization via the :orgId URL parameter.
|
|
184
|
-
*
|
|
185
|
-
*
|
|
177
|
+
* Operations that cannot preserve bootstrap-administrator availability are rejected at their
|
|
178
|
+
* owning service or repository boundary.
|
|
186
179
|
*
|
|
187
180
|
* Prefix: /api/admin/organizations/:orgId/users
|
|
188
181
|
*
|
|
@@ -212,7 +205,7 @@ export function createUserRouter() {
|
|
|
212
205
|
// -------------------------------------------------------------------------
|
|
213
206
|
// GET / — List users (paginated)
|
|
214
207
|
// -------------------------------------------------------------------------
|
|
215
|
-
router.get('/', requirePermission(ADMIN_PERMISSIONS.USER_READ), async (ctx) => {
|
|
208
|
+
router.get('/', requirePermission(ADMIN_PERMISSIONS.USER_READ), requireExistingOrganization(), async (ctx) => {
|
|
216
209
|
try {
|
|
217
210
|
// Cursor-based pagination when `cursor` or `limit` param is present
|
|
218
211
|
if (ctx.query.cursor !== undefined || ctx.query.limit !== undefined) {
|
|
@@ -273,7 +266,7 @@ export function createUserRouter() {
|
|
|
273
266
|
// POST /:userId/deactivate — Deactivate user
|
|
274
267
|
// Protected: super-admin user cannot be deactivated
|
|
275
268
|
// -------------------------------------------------------------------------
|
|
276
|
-
router.post('/:userId/deactivate', requirePermission(ADMIN_PERMISSIONS.
|
|
269
|
+
router.post('/:userId/deactivate', requirePermission(ADMIN_PERMISSIONS.USER_LIFECYCLE), requireUserOrganization(), async (ctx) => {
|
|
277
270
|
try {
|
|
278
271
|
await guardSuperAdmin(ctx.params.userId, 'deactivate');
|
|
279
272
|
await userService.deactivateUser(ctx.params.userId);
|
|
@@ -284,65 +277,11 @@ export function createUserRouter() {
|
|
|
284
277
|
}
|
|
285
278
|
});
|
|
286
279
|
// -------------------------------------------------------------------------
|
|
287
|
-
// POST /:userId/
|
|
288
|
-
// -------------------------------------------------------------------------
|
|
289
|
-
router.post('/:userId/reactivate', requirePermission(ADMIN_PERMISSIONS.USER_SUSPEND), requireUserOrganization(), async (ctx) => {
|
|
290
|
-
try {
|
|
291
|
-
await userService.reactivateUser(ctx.params.userId);
|
|
292
|
-
ctx.status = 204;
|
|
293
|
-
}
|
|
294
|
-
catch (err) {
|
|
295
|
-
handleError(ctx, err);
|
|
296
|
-
}
|
|
297
|
-
});
|
|
298
|
-
// -------------------------------------------------------------------------
|
|
299
|
-
// POST /:userId/suspend — Suspend user
|
|
300
|
-
// Protected: super-admin user cannot be suspended
|
|
301
|
-
// -------------------------------------------------------------------------
|
|
302
|
-
router.post('/:userId/suspend', requirePermission(ADMIN_PERMISSIONS.USER_SUSPEND), requireUserOrganization(), async (ctx) => {
|
|
303
|
-
try {
|
|
304
|
-
await guardSuperAdmin(ctx.params.userId, 'suspend');
|
|
305
|
-
const body = suspendUserSchema.parse(ctx.request.body ?? {});
|
|
306
|
-
await userService.suspendUser(ctx.params.userId, body.reason);
|
|
307
|
-
ctx.status = 204;
|
|
308
|
-
}
|
|
309
|
-
catch (err) {
|
|
310
|
-
handleError(ctx, err);
|
|
311
|
-
}
|
|
312
|
-
});
|
|
313
|
-
// -------------------------------------------------------------------------
|
|
314
|
-
// POST /:userId/unsuspend — Unsuspend user
|
|
315
|
-
// -------------------------------------------------------------------------
|
|
316
|
-
router.post('/:userId/unsuspend', requirePermission(ADMIN_PERMISSIONS.USER_SUSPEND), requireUserOrganization(), async (ctx) => {
|
|
317
|
-
try {
|
|
318
|
-
await userService.unsuspendUser(ctx.params.userId);
|
|
319
|
-
ctx.status = 204;
|
|
320
|
-
}
|
|
321
|
-
catch (err) {
|
|
322
|
-
handleError(ctx, err);
|
|
323
|
-
}
|
|
324
|
-
});
|
|
325
|
-
// -------------------------------------------------------------------------
|
|
326
|
-
// POST /:userId/lock — Lock user
|
|
327
|
-
// Protected: super-admin user cannot be locked
|
|
280
|
+
// POST /:userId/activate — Activate user
|
|
328
281
|
// -------------------------------------------------------------------------
|
|
329
|
-
router.post('/:userId/
|
|
282
|
+
router.post('/:userId/activate', requirePermission(ADMIN_PERMISSIONS.USER_LIFECYCLE), requireUserOrganization(), async (ctx) => {
|
|
330
283
|
try {
|
|
331
|
-
await
|
|
332
|
-
const body = lockUserSchema.parse(ctx.request.body);
|
|
333
|
-
await userService.lockUser(ctx.params.userId, body.reason);
|
|
334
|
-
ctx.status = 204;
|
|
335
|
-
}
|
|
336
|
-
catch (err) {
|
|
337
|
-
handleError(ctx, err);
|
|
338
|
-
}
|
|
339
|
-
});
|
|
340
|
-
// -------------------------------------------------------------------------
|
|
341
|
-
// POST /:userId/unlock — Unlock user
|
|
342
|
-
// -------------------------------------------------------------------------
|
|
343
|
-
router.post('/:userId/unlock', requirePermission(ADMIN_PERMISSIONS.USER_SUSPEND), requireUserOrganization(), async (ctx) => {
|
|
344
|
-
try {
|
|
345
|
-
await userService.unlockUser(ctx.params.userId);
|
|
284
|
+
await userService.activateUser(ctx.params.userId);
|
|
346
285
|
ctx.status = 204;
|
|
347
286
|
}
|
|
348
287
|
catch (err) {
|
|
@@ -398,48 +337,16 @@ export function createUserRouter() {
|
|
|
398
337
|
ctx.body = { data: exportData };
|
|
399
338
|
});
|
|
400
339
|
// -------------------------------------------------------------------------
|
|
401
|
-
//
|
|
402
|
-
//
|
|
403
|
-
// Requires X-Confirm-Purge: true header for safety.
|
|
404
|
-
// Irreversibly anonymizes user data and deletes related records.
|
|
405
|
-
// Protected: super-admin user cannot be purged
|
|
340
|
+
// DELETE /:userId — Delete user
|
|
406
341
|
// -------------------------------------------------------------------------
|
|
407
|
-
router.
|
|
408
|
-
// Require explicit confirmation via header OR request body
|
|
409
|
-
const confirmHeader = ctx.get('X-Confirm-Purge');
|
|
410
|
-
const confirmBody = ctx.request.body?.confirmPurge;
|
|
411
|
-
if (confirmHeader !== 'true' && confirmBody !== true) {
|
|
412
|
-
ctx.status = 400;
|
|
413
|
-
ctx.body = {
|
|
414
|
-
error: 'Purge requires confirmation',
|
|
415
|
-
message: 'Set X-Confirm-Purge: true header or send { "confirmPurge": true } in body',
|
|
416
|
-
};
|
|
417
|
-
return;
|
|
418
|
-
}
|
|
419
|
-
const user = await userService.getUserById(ctx.params.userId);
|
|
420
|
-
if (!user) {
|
|
421
|
-
return ctx.throw(404, 'User not found');
|
|
422
|
-
}
|
|
342
|
+
router.delete('/:userId', requirePermission(ADMIN_PERMISSIONS.USER_DELETE), requireUserOrganization(), async (ctx) => {
|
|
423
343
|
try {
|
|
424
|
-
|
|
425
|
-
await
|
|
426
|
-
|
|
427
|
-
const actorId = ctx.state.adminUser?.id ?? 'system';
|
|
428
|
-
const result = await purgeUserData(user, actorId);
|
|
429
|
-
ctx.body = { data: result };
|
|
344
|
+
identifierSchema.parse(ctx.params);
|
|
345
|
+
await userService.deleteUser(ctx.params.orgId, ctx.params.userId, ctx.state.adminUser?.id);
|
|
346
|
+
ctx.status = 204;
|
|
430
347
|
}
|
|
431
348
|
catch (err) {
|
|
432
|
-
|
|
433
|
-
ctx.status = 403;
|
|
434
|
-
ctx.body = { error: 'The requested operation is not permitted' };
|
|
435
|
-
return;
|
|
436
|
-
}
|
|
437
|
-
if (err instanceof Error && err.message.includes('super-admin')) {
|
|
438
|
-
ctx.status = 403;
|
|
439
|
-
ctx.body = { error: 'The requested operation is not permitted' };
|
|
440
|
-
return;
|
|
441
|
-
}
|
|
442
|
-
throw err;
|
|
349
|
+
handleError(ctx, err);
|
|
443
350
|
}
|
|
444
351
|
});
|
|
445
352
|
// -------------------------------------------------------------------------
|
|
@@ -455,12 +362,13 @@ export function createUserRouter() {
|
|
|
455
362
|
ctx.body = result;
|
|
456
363
|
});
|
|
457
364
|
// -------------------------------------------------------------------------
|
|
458
|
-
// POST /invite —
|
|
365
|
+
// POST /invite — Invite a new user
|
|
459
366
|
//
|
|
460
367
|
// Enhanced invitation with optional personal message, role/claim
|
|
461
368
|
// pre-assignment, and inviter tracking. Creates the user if they
|
|
462
369
|
// don't exist, generates an invitation token with pre-assignment
|
|
463
|
-
// details, and sends the invitation email.
|
|
370
|
+
// details, and sends the invitation email. Existing organization email
|
|
371
|
+
// addresses are rejected instead of silently resending an invitation.
|
|
464
372
|
// -------------------------------------------------------------------------
|
|
465
373
|
router.post('/invite', requirePermission(ADMIN_PERMISSIONS.USER_INVITE), async (ctx) => {
|
|
466
374
|
try {
|
|
@@ -475,25 +383,30 @@ export function createUserRouter() {
|
|
|
475
383
|
}
|
|
476
384
|
// Validate referenced applicationIds, roleIds, claimDefinitionIds exist
|
|
477
385
|
if (body.roles?.length || body.claims?.length) {
|
|
478
|
-
await validatePreAssignments(
|
|
386
|
+
const preAssignmentErrors = await validatePreAssignments(body.roles, body.claims);
|
|
387
|
+
if (preAssignmentErrors.length > 0) {
|
|
388
|
+
ctx.status = 400;
|
|
389
|
+
ctx.body = { error: 'User request is invalid' };
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
479
392
|
}
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
organizationId: orgId,
|
|
486
|
-
email: body.email,
|
|
487
|
-
givenName: body.givenName,
|
|
488
|
-
familyName: body.familyName,
|
|
489
|
-
});
|
|
490
|
-
created = true;
|
|
393
|
+
const existingUser = await userService.getUserByEmail(orgId, body.email);
|
|
394
|
+
if (existingUser) {
|
|
395
|
+
ctx.status = 409;
|
|
396
|
+
ctx.body = { error: 'User already exists in this organization' };
|
|
397
|
+
return;
|
|
491
398
|
}
|
|
492
|
-
|
|
493
|
-
|
|
399
|
+
const user = await userService.createUser({
|
|
400
|
+
organizationId: orgId,
|
|
401
|
+
email: body.email,
|
|
402
|
+
givenName: body.givenName,
|
|
403
|
+
familyName: body.familyName,
|
|
404
|
+
});
|
|
494
405
|
// Generate a new invitation token
|
|
495
406
|
const { plaintext, hash } = generateToken();
|
|
496
|
-
|
|
407
|
+
// Existing invitations keep their absolute expiry; this policy applies to the new token only.
|
|
408
|
+
const invitationTtl = await getSystemConfigNumber('invitation_ttl');
|
|
409
|
+
const expiresAt = new Date(Date.now() + invitationTtl * 1000);
|
|
497
410
|
// Build inviter display name
|
|
498
411
|
const inviterName = adminUser.givenName
|
|
499
412
|
? adminUser.familyName
|
|
@@ -524,13 +437,7 @@ export function createUserRouter() {
|
|
|
524
437
|
email: user.email,
|
|
525
438
|
givenName: user.givenName,
|
|
526
439
|
familyName: user.familyName,
|
|
527
|
-
},
|
|
528
|
-
id: org.id,
|
|
529
|
-
slug: org.slug,
|
|
530
|
-
brandingLogoUrl: org.brandingLogoUrl,
|
|
531
|
-
brandingPrimaryColor: org.brandingPrimaryColor,
|
|
532
|
-
brandingCompanyName: org.brandingCompanyName,
|
|
533
|
-
}, inviteUrl, body.locale ?? org.defaultLocale ?? 'en', emailOptions));
|
|
440
|
+
}, org, inviteUrl, body.locale ?? org.defaultLocale ?? 'en', emailOptions));
|
|
534
441
|
// Audit log the invitation
|
|
535
442
|
writeAuditLog({
|
|
536
443
|
organizationId: orgId,
|
|
@@ -545,12 +452,12 @@ export function createUserRouter() {
|
|
|
545
452
|
preAssignedClaims: body.claims?.length ?? 0,
|
|
546
453
|
},
|
|
547
454
|
});
|
|
548
|
-
ctx.status =
|
|
455
|
+
ctx.status = 201;
|
|
549
456
|
ctx.body = {
|
|
550
457
|
data: {
|
|
551
458
|
userId: user.id,
|
|
552
459
|
email: user.email,
|
|
553
|
-
created,
|
|
460
|
+
created: true,
|
|
554
461
|
invitationSent: true,
|
|
555
462
|
expiresAt: expiresAt.toISOString(),
|
|
556
463
|
},
|
|
@@ -595,13 +502,7 @@ export function createUserRouter() {
|
|
|
595
502
|
if (body.personalMessage)
|
|
596
503
|
emailOptions.personalMessage = body.personalMessage;
|
|
597
504
|
emailOptions.inviterName = inviterName;
|
|
598
|
-
const result = await renderInvitationEmail(previewUser,
|
|
599
|
-
id: org.id,
|
|
600
|
-
slug: org.slug,
|
|
601
|
-
brandingLogoUrl: org.brandingLogoUrl,
|
|
602
|
-
brandingPrimaryColor: org.brandingPrimaryColor,
|
|
603
|
-
brandingCompanyName: org.brandingCompanyName,
|
|
604
|
-
},
|
|
505
|
+
const result = await renderInvitationEmail(previewUser, org,
|
|
605
506
|
// Absolute preview URL — same trusted issuerBaseUrl prefix as the real invite.
|
|
606
507
|
`${config.issuerBaseUrl}/${org.slug}/auth/accept-invite/PREVIEW_TOKEN`, body.locale ?? org.defaultLocale ?? 'en', emailOptions);
|
|
607
508
|
ctx.body = { data: result };
|
|
@@ -650,30 +551,30 @@ const invitePreviewSchema = z.object({
|
|
|
650
551
|
/**
|
|
651
552
|
* Validate that referenced roles and claims exist in the database.
|
|
652
553
|
*
|
|
653
|
-
* Checks that all applicationIds
|
|
554
|
+
* Checks that all deployment-global applicationIds exist, all roleIds
|
|
654
555
|
* belong to their specified application, and all claimDefinitionIds
|
|
655
|
-
* belong to their specified application.
|
|
656
|
-
* on validation failure.
|
|
556
|
+
* belong to their specified application.
|
|
657
557
|
*
|
|
658
|
-
* @param orgId - Organization ID for scoping
|
|
659
558
|
* @param roles - Array of role pre-assignments to validate
|
|
660
559
|
* @param claims - Array of claim pre-assignments to validate
|
|
661
|
-
* @
|
|
560
|
+
* @returns Validation errors; an empty array means every reference is valid
|
|
662
561
|
*/
|
|
663
|
-
async function validatePreAssignments(
|
|
562
|
+
async function validatePreAssignments(roles, claims) {
|
|
664
563
|
const pool = getPool();
|
|
665
564
|
const errors = [];
|
|
666
565
|
// Collect unique applicationIds from both roles and claims
|
|
667
566
|
const appIds = new Set();
|
|
668
567
|
roles?.forEach((r) => appIds.add(r.applicationId));
|
|
669
568
|
claims?.forEach((c) => appIds.add(c.applicationId));
|
|
670
|
-
//
|
|
569
|
+
// Applications are deployment-global definitions and are not organization-scoped.
|
|
671
570
|
if (appIds.size > 0) {
|
|
672
|
-
const appResult = await pool.query(`SELECT id FROM applications WHERE id = ANY($1)
|
|
571
|
+
const appResult = await pool.query(`SELECT id FROM applications WHERE id = ANY($1::uuid[])`, [
|
|
572
|
+
Array.from(appIds),
|
|
573
|
+
]);
|
|
673
574
|
const foundIds = new Set(appResult.rows.map((r) => r.id));
|
|
674
575
|
for (const appId of appIds) {
|
|
675
576
|
if (!foundIds.has(appId)) {
|
|
676
|
-
errors.push(`Application ${appId} not found
|
|
577
|
+
errors.push(`Application ${appId} not found`);
|
|
677
578
|
}
|
|
678
579
|
}
|
|
679
580
|
}
|
|
@@ -689,17 +590,13 @@ async function validatePreAssignments(orgId, roles, claims) {
|
|
|
689
590
|
// Verify all claim definitions exist within their application
|
|
690
591
|
if (claims?.length) {
|
|
691
592
|
for (const claim of claims) {
|
|
692
|
-
const result = await pool.query(`SELECT id FROM
|
|
593
|
+
const result = await pool.query(`SELECT id FROM custom_claim_definitions WHERE id = $1 AND application_id = $2`, [claim.claimDefinitionId, claim.applicationId]);
|
|
693
594
|
if (result.rows.length === 0) {
|
|
694
595
|
errors.push(`Claim definition ${claim.claimDefinitionId} not found in application ${claim.applicationId}`);
|
|
695
596
|
}
|
|
696
597
|
}
|
|
697
598
|
}
|
|
698
|
-
|
|
699
|
-
const err = new Error(`Pre-assignment validation failed: ${errors.join('; ')}`);
|
|
700
|
-
err.status = 400;
|
|
701
|
-
throw err;
|
|
702
|
-
}
|
|
599
|
+
return errors;
|
|
703
600
|
}
|
|
704
601
|
// ---------------------------------------------------------------------------
|
|
705
602
|
// Standalone user router (non-org-scoped)
|
|
@@ -748,7 +645,7 @@ export function createStandaloneUserRouter() {
|
|
|
748
645
|
}
|
|
749
646
|
});
|
|
750
647
|
// POST /:userId/deactivate — Deactivate user
|
|
751
|
-
router.post('/:userId/deactivate', requirePermission(ADMIN_PERMISSIONS.
|
|
648
|
+
router.post('/:userId/deactivate', requirePermission(ADMIN_PERMISSIONS.USER_LIFECYCLE), async (ctx) => {
|
|
752
649
|
try {
|
|
753
650
|
await guardSuperAdmin(ctx.params.userId, 'deactivate');
|
|
754
651
|
await userService.deactivateUser(ctx.params.userId);
|
|
@@ -758,64 +655,10 @@ export function createStandaloneUserRouter() {
|
|
|
758
655
|
handleError(ctx, err);
|
|
759
656
|
}
|
|
760
657
|
});
|
|
761
|
-
// POST /:userId/
|
|
762
|
-
router.post('/:userId/
|
|
763
|
-
try {
|
|
764
|
-
await userService.reactivateUser(ctx.params.userId);
|
|
765
|
-
ctx.status = 204;
|
|
766
|
-
}
|
|
767
|
-
catch (err) {
|
|
768
|
-
handleError(ctx, err);
|
|
769
|
-
}
|
|
770
|
-
});
|
|
771
|
-
// POST /:userId/activate — Alias for reactivate (SPA compatibility)
|
|
772
|
-
router.post('/:userId/activate', requirePermission(ADMIN_PERMISSIONS.USER_SUSPEND), async (ctx) => {
|
|
773
|
-
try {
|
|
774
|
-
await userService.reactivateUser(ctx.params.userId);
|
|
775
|
-
ctx.status = 204;
|
|
776
|
-
}
|
|
777
|
-
catch (err) {
|
|
778
|
-
handleError(ctx, err);
|
|
779
|
-
}
|
|
780
|
-
});
|
|
781
|
-
// POST /:userId/suspend — Suspend user
|
|
782
|
-
router.post('/:userId/suspend', requirePermission(ADMIN_PERMISSIONS.USER_SUSPEND), async (ctx) => {
|
|
783
|
-
try {
|
|
784
|
-
await guardSuperAdmin(ctx.params.userId, 'suspend');
|
|
785
|
-
const body = suspendUserSchema.parse(ctx.request.body ?? {});
|
|
786
|
-
await userService.suspendUser(ctx.params.userId, body.reason);
|
|
787
|
-
ctx.status = 204;
|
|
788
|
-
}
|
|
789
|
-
catch (err) {
|
|
790
|
-
handleError(ctx, err);
|
|
791
|
-
}
|
|
792
|
-
});
|
|
793
|
-
// POST /:userId/unsuspend — Unsuspend user
|
|
794
|
-
router.post('/:userId/unsuspend', requirePermission(ADMIN_PERMISSIONS.USER_SUSPEND), async (ctx) => {
|
|
795
|
-
try {
|
|
796
|
-
await userService.unsuspendUser(ctx.params.userId);
|
|
797
|
-
ctx.status = 204;
|
|
798
|
-
}
|
|
799
|
-
catch (err) {
|
|
800
|
-
handleError(ctx, err);
|
|
801
|
-
}
|
|
802
|
-
});
|
|
803
|
-
// POST /:userId/lock — Lock user
|
|
804
|
-
router.post('/:userId/lock', requirePermission(ADMIN_PERMISSIONS.USER_SUSPEND), async (ctx) => {
|
|
805
|
-
try {
|
|
806
|
-
await guardSuperAdmin(ctx.params.userId, 'lock');
|
|
807
|
-
const body = lockUserSchema.parse(ctx.request.body);
|
|
808
|
-
await userService.lockUser(ctx.params.userId, body.reason);
|
|
809
|
-
ctx.status = 204;
|
|
810
|
-
}
|
|
811
|
-
catch (err) {
|
|
812
|
-
handleError(ctx, err);
|
|
813
|
-
}
|
|
814
|
-
});
|
|
815
|
-
// POST /:userId/unlock — Unlock user
|
|
816
|
-
router.post('/:userId/unlock', requirePermission(ADMIN_PERMISSIONS.USER_SUSPEND), async (ctx) => {
|
|
658
|
+
// POST /:userId/activate — Activate user (inactive → active)
|
|
659
|
+
router.post('/:userId/activate', requirePermission(ADMIN_PERMISSIONS.USER_LIFECYCLE), async (ctx) => {
|
|
817
660
|
try {
|
|
818
|
-
await userService.
|
|
661
|
+
await userService.activateUser(ctx.params.userId);
|
|
819
662
|
ctx.status = 204;
|
|
820
663
|
}
|
|
821
664
|
catch (err) {
|