alepha 0.19.0 → 0.19.2
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/LICENSE +1 -1
- package/README.md +6 -9
- package/dist/api/audits/index.d.ts +378 -346
- package/dist/api/audits/index.d.ts.map +1 -1
- package/dist/api/files/index.d.ts +216 -184
- package/dist/api/files/index.d.ts.map +1 -1
- package/dist/api/files/index.js +5 -1
- package/dist/api/files/index.js.map +1 -1
- package/dist/api/jobs/index.d.ts +528 -496
- package/dist/api/jobs/index.d.ts.map +1 -1
- package/dist/api/jobs/index.js +3 -3
- package/dist/api/jobs/index.js.map +1 -1
- package/dist/api/keys/index.d.ts +202 -202
- package/dist/api/keys/index.d.ts.map +1 -1
- package/dist/api/notifications/index.d.ts +152 -152
- package/dist/api/notifications/index.d.ts.map +1 -1
- package/dist/api/organizations/index.browser.js +48 -0
- package/dist/api/organizations/index.browser.js.map +1 -0
- package/dist/api/organizations/index.d.ts +516 -0
- package/dist/api/organizations/index.d.ts.map +1 -0
- package/dist/api/organizations/index.js +202 -0
- package/dist/api/organizations/index.js.map +1 -0
- package/dist/api/parameters/index.d.ts +391 -358
- package/dist/api/parameters/index.d.ts.map +1 -1
- package/dist/api/parameters/index.js +5 -1
- package/dist/api/parameters/index.js.map +1 -1
- package/dist/api/users/index.browser.js +7 -5
- package/dist/api/users/index.browser.js.map +1 -1
- package/dist/api/users/index.d.ts +989 -931
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/users/index.js +292 -146
- package/dist/api/users/index.js.map +1 -1
- package/dist/api/verifications/index.d.ts +132 -132
- package/dist/api/verifications/index.d.ts.map +1 -1
- package/dist/api/verifications/index.js +2 -2
- package/dist/api/verifications/index.js.map +1 -1
- package/dist/batch/index.d.ts +6 -6
- package/dist/batch/index.d.ts.map +1 -1
- package/dist/billing/index.d.ts +1048 -0
- package/dist/billing/index.d.ts.map +1 -0
- package/dist/billing/index.js +713 -0
- package/dist/billing/index.js.map +1 -0
- package/dist/bin/index.js +0 -2
- package/dist/bin/index.js.map +1 -1
- package/dist/bucket/index.d.ts +10 -10
- package/dist/bucket/index.d.ts.map +1 -1
- package/dist/bucket/index.js +2 -2
- package/dist/bucket/index.js.map +1 -1
- package/dist/cache/core/index.d.ts +9 -9
- package/dist/cache/core/index.d.ts.map +1 -1
- package/dist/cache/core/index.js +2 -2
- package/dist/cache/core/index.js.map +1 -1
- package/dist/cache/core/index.workerd.js +2 -2
- package/dist/cache/core/index.workerd.js.map +1 -1
- package/dist/cache/redis/index.d.ts +6 -6
- package/dist/cache/redis/index.d.ts.map +1 -1
- package/dist/cache/redis/index.js +2 -2
- package/dist/cache/redis/index.js.map +1 -1
- package/dist/cli/config/index.d.ts +12 -2
- package/dist/cli/config/index.d.ts.map +1 -1
- package/dist/cli/config/index.js +4 -0
- package/dist/cli/config/index.js.map +1 -1
- package/dist/cli/core/index.d.ts +183 -139
- package/dist/cli/core/index.d.ts.map +1 -1
- package/dist/cli/core/index.js +283 -86
- package/dist/cli/core/index.js.map +1 -1
- package/dist/cli/devtools/index.d.ts +45 -0
- package/dist/cli/devtools/index.d.ts.map +1 -0
- package/dist/cli/devtools/index.js +170 -0
- package/dist/cli/devtools/index.js.map +1 -0
- package/dist/cli/platform/index.d.ts +383 -492
- package/dist/cli/platform/index.d.ts.map +1 -1
- package/dist/cli/platform/index.js +42 -511
- package/dist/cli/platform/index.js.map +1 -1
- package/dist/cli/vendor/index.d.ts +196 -0
- package/dist/cli/vendor/index.d.ts.map +1 -0
- package/dist/cli/vendor/index.js +384 -0
- package/dist/cli/vendor/index.js.map +1 -0
- package/dist/command/index.d.ts +18 -18
- package/dist/command/index.d.ts.map +1 -1
- package/dist/command/index.js +2 -2
- package/dist/command/index.js.map +1 -1
- package/dist/core/index.browser.js +4 -4
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +10 -10
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +4 -4
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +4 -4
- package/dist/core/index.native.js.map +1 -1
- package/dist/core/index.workerd.js +4 -4
- package/dist/core/index.workerd.js.map +1 -1
- package/dist/crypto/index.d.ts +7 -7
- package/dist/crypto/index.d.ts.map +1 -1
- package/dist/datetime/index.d.ts +4 -4
- package/dist/datetime/index.d.ts.map +1 -1
- package/dist/email/brevo/index.d.ts +4 -4
- package/dist/email/brevo/index.d.ts.map +1 -1
- package/dist/email/core/index.d.ts +15 -11
- package/dist/email/core/index.d.ts.map +1 -1
- package/dist/email/core/index.js +12 -35
- package/dist/email/core/index.js.map +1 -1
- package/dist/email/smtp/index.d.ts +12 -12
- package/dist/email/smtp/index.d.ts.map +1 -1
- package/dist/email/smtp/index.js +7 -4
- package/dist/email/smtp/index.js.map +1 -1
- package/dist/fake/index.d.ts +4 -8
- package/dist/fake/index.d.ts.map +1 -1
- package/dist/fake/index.js +55 -889
- package/dist/fake/index.js.map +1 -1
- package/dist/lock/core/index.d.ts +13 -13
- package/dist/lock/core/index.d.ts.map +1 -1
- package/dist/lock/core/index.js +2 -2
- package/dist/lock/core/index.js.map +1 -1
- package/dist/lock/redis/index.d.ts +4 -4
- package/dist/lock/redis/index.d.ts.map +1 -1
- package/dist/logger/index.d.ts +16 -15
- package/dist/logger/index.d.ts.map +1 -1
- package/dist/logger/index.js +5 -2
- package/dist/logger/index.js.map +1 -1
- package/dist/mcp/index.d.ts +11 -11
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +2 -2
- package/dist/mcp/index.js.map +1 -1
- package/dist/orm/core/index.browser.js +11 -1
- package/dist/orm/core/index.browser.js.map +1 -1
- package/dist/orm/core/index.bun.js +53 -16
- package/dist/orm/core/index.bun.js.map +1 -1
- package/dist/orm/core/index.d.ts +95 -51
- package/dist/orm/core/index.d.ts.map +1 -1
- package/dist/orm/core/index.js +55 -14
- package/dist/orm/core/index.js.map +1 -1
- package/dist/orm/postgres/index.bun.js +17 -11
- package/dist/orm/postgres/index.bun.js.map +1 -1
- package/dist/orm/postgres/index.d.ts +21 -15
- package/dist/orm/postgres/index.d.ts.map +1 -1
- package/dist/orm/postgres/index.js +19 -13
- package/dist/orm/postgres/index.js.map +1 -1
- package/dist/queue/core/index.d.ts +14 -14
- package/dist/queue/core/index.d.ts.map +1 -1
- package/dist/queue/core/index.js +2 -2
- package/dist/queue/core/index.js.map +1 -1
- package/dist/queue/core/index.workerd.js +2 -2
- package/dist/queue/core/index.workerd.js.map +1 -1
- package/dist/queue/redis/index.d.ts +4 -4
- package/dist/queue/redis/index.d.ts.map +1 -1
- package/dist/queue/redis/index.js +2 -2
- package/dist/queue/redis/index.js.map +1 -1
- package/dist/react/auth/index.d.ts +9 -9
- package/dist/react/auth/index.d.ts.map +1 -1
- package/dist/react/core/index.d.ts +6 -6
- package/dist/react/core/index.d.ts.map +1 -1
- package/dist/react/core/index.js +5 -4
- package/dist/react/core/index.js.map +1 -1
- package/dist/react/form/index.d.ts +4 -4
- package/dist/react/form/index.d.ts.map +1 -1
- package/dist/react/head/index.d.ts +4 -4
- package/dist/react/head/index.d.ts.map +1 -1
- package/dist/react/i18n/index.d.ts +9 -9
- package/dist/react/i18n/index.d.ts.map +1 -1
- package/dist/react/intro/index.d.ts +2 -2
- package/dist/react/intro/index.d.ts.map +1 -1
- package/dist/react/intro/index.js +3 -4
- package/dist/react/intro/index.js.map +1 -1
- package/dist/react/router/index.browser.js +4 -5
- package/dist/react/router/index.browser.js.map +1 -1
- package/dist/react/router/index.d.ts +215 -215
- package/dist/react/router/index.d.ts.map +1 -1
- package/dist/react/router/index.js +6 -7
- package/dist/react/router/index.js.map +1 -1
- package/dist/react/testing/index.d.ts +2 -2
- package/dist/react/testing/index.d.ts.map +1 -1
- package/dist/react/testing/index.js +2 -4
- package/dist/react/testing/index.js.map +1 -1
- package/dist/redis/index.d.ts +19 -19
- package/dist/redis/index.d.ts.map +1 -1
- package/dist/retry/index.d.ts +4 -4
- package/dist/retry/index.d.ts.map +1 -1
- package/dist/scheduler/index.d.ts +13 -13
- package/dist/scheduler/index.d.ts.map +1 -1
- package/dist/scheduler/index.js +2 -2
- package/dist/scheduler/index.js.map +1 -1
- package/dist/scheduler/index.workerd.js +2 -2
- package/dist/scheduler/index.workerd.js.map +1 -1
- package/dist/security/index.browser.js +1 -1
- package/dist/security/index.browser.js.map +1 -1
- package/dist/security/index.d.ts +47 -47
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js +9 -12
- package/dist/security/index.js.map +1 -1
- package/dist/server/auth/index.d.ts +170 -169
- package/dist/server/auth/index.d.ts.map +1 -1
- package/dist/server/auth/index.js +16 -2
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/cookies/index.d.ts +7 -7
- package/dist/server/cookies/index.d.ts.map +1 -1
- package/dist/server/core/index.d.ts +76 -76
- package/dist/server/core/index.d.ts.map +1 -1
- package/dist/server/core/index.js +23 -17
- package/dist/server/core/index.js.map +1 -1
- package/dist/server/cors/index.d.ts +13 -13
- package/dist/server/cors/index.d.ts.map +1 -1
- package/dist/server/cors/index.js +2 -2
- package/dist/server/cors/index.js.map +1 -1
- package/dist/server/etag/index.d.ts +9 -9
- package/dist/server/etag/index.d.ts.map +1 -1
- package/dist/server/health/index.d.ts +20 -20
- package/dist/server/health/index.d.ts.map +1 -1
- package/dist/server/links/index.browser.js +2 -2
- package/dist/server/links/index.browser.js.map +1 -1
- package/dist/server/links/index.d.ts +66 -66
- package/dist/server/links/index.d.ts.map +1 -1
- package/dist/server/links/index.js +4 -4
- package/dist/server/links/index.js.map +1 -1
- package/dist/server/metrics/index.d.ts +7 -7
- package/dist/server/metrics/index.d.ts.map +1 -1
- package/dist/server/proxy/index.d.ts +5 -5
- package/dist/server/proxy/index.d.ts.map +1 -1
- package/dist/server/rate-limit/index.d.ts +12 -12
- package/dist/server/rate-limit/index.d.ts.map +1 -1
- package/dist/server/rate-limit/index.js +2 -2
- package/dist/server/rate-limit/index.js.map +1 -1
- package/dist/server/static/index.d.ts +5 -5
- package/dist/server/static/index.d.ts.map +1 -1
- package/dist/server/swagger/index.d.ts +7 -7
- package/dist/server/swagger/index.d.ts.map +1 -1
- package/dist/server/swagger/index.js +2 -2
- package/dist/server/swagger/index.js.map +1 -1
- package/dist/sms/index.d.ts +11 -7
- package/dist/sms/index.d.ts.map +1 -1
- package/dist/sms/index.js +9 -15
- package/dist/sms/index.js.map +1 -1
- package/dist/system/index.d.ts +4 -4
- package/dist/system/index.d.ts.map +1 -1
- package/dist/topic/core/index.d.ts +6 -6
- package/dist/topic/core/index.d.ts.map +1 -1
- package/dist/topic/redis/index.d.ts +7 -7
- package/dist/topic/redis/index.d.ts.map +1 -1
- package/dist/topic/redis/index.js +2 -2
- package/dist/topic/redis/index.js.map +1 -1
- package/dist/websocket/index.d.ts +36 -36
- package/dist/websocket/index.d.ts.map +1 -1
- package/dist/websocket/index.js +2 -2
- package/dist/websocket/index.js.map +1 -1
- package/package.json +37 -32
- package/src/api/files/{controllers → __tests__}/FileController.spec.ts +189 -143
- package/src/api/files/{services → __tests__}/FileService.spec.ts +50 -30
- package/src/api/files/controllers/FileController.ts +6 -0
- package/src/api/jobs/{services → __tests__}/JobService.spec.ts +1 -1
- package/src/api/jobs/providers/JobProvider.ts +3 -3
- package/src/api/keys/{services → __tests__}/ApiKeyService.spec.ts +1 -1
- package/src/api/organizations/__tests__/OrganizationService.spec.ts +193 -0
- package/src/api/organizations/controllers/AdminOrganizationController.ts +103 -0
- package/src/api/organizations/entities/organizations.ts +20 -0
- package/src/api/organizations/index.browser.ts +10 -0
- package/src/api/organizations/index.ts +31 -0
- package/src/api/organizations/schemas/createOrganizationSchema.ts +10 -0
- package/src/api/organizations/schemas/organizationQuerySchema.ts +10 -0
- package/src/api/organizations/schemas/organizationResourceSchema.ts +6 -0
- package/src/api/organizations/schemas/updateOrganizationSchema.ts +7 -0
- package/src/api/organizations/services/OrganizationService.ts +75 -0
- package/src/api/parameters/services/ParameterProvider.ts +6 -1
- package/src/api/users/__tests__/$realm.spec.ts +191 -0
- package/src/api/users/__tests__/RealmProvider.spec.ts +53 -0
- package/src/api/users/__tests__/SessionService.spec.ts +778 -0
- package/src/api/users/{jobs → __tests__}/UserJobs.spec.ts +1 -1
- package/src/api/users/atoms/realmAuthSettingsAtom.ts +24 -0
- package/src/api/users/controllers/RealmController.ts +5 -11
- package/src/api/users/entities/users.ts +9 -3
- package/src/api/users/index.ts +23 -3
- package/src/api/users/primitives/$realm.ts +23 -11
- package/src/api/users/providers/RealmProvider.ts +23 -3
- package/src/api/users/services/RegistrationService.ts +3 -2
- package/src/api/users/services/SessionService.ts +249 -8
- package/src/api/users/services/UserService.ts +1 -1
- package/src/api/verifications/{jobs → __tests__}/VerificationJobs.spec.ts +4 -2
- package/src/api/verifications/parameters/VerificationParameters.ts +2 -2
- package/src/billing/__tests__/BillingService.spec.ts +136 -0
- package/src/billing/__tests__/PaymentMethodService.spec.ts +78 -0
- package/src/billing/controllers/AdminBillingController.ts +149 -0
- package/src/billing/controllers/BillingController.ts +108 -0
- package/src/billing/entities/paymentIntents.ts +34 -0
- package/src/billing/entities/paymentMethods.ts +24 -0
- package/src/billing/entities/refunds.ts +22 -0
- package/src/billing/errors/BillingError.ts +5 -0
- package/src/billing/index.ts +76 -0
- package/src/billing/providers/BillingProvider.ts +79 -0
- package/src/billing/providers/MemoryBillingProvider.ts +139 -0
- package/src/billing/schemas/intentSchemas.ts +60 -0
- package/src/billing/schemas/paymentMethodSchemas.ts +13 -0
- package/src/billing/schemas/refundSchemas.ts +6 -0
- package/src/billing/services/BillingService.ts +325 -0
- package/src/billing/services/PaymentMethodService.ts +82 -0
- package/src/bin/index.ts +0 -2
- package/src/bucket/providers/LocalFileStorageProvider.ts +2 -2
- package/src/cache/core/{primitives → __tests__}/$cache.middleware.spec.ts +1 -1
- package/src/cache/core/{providers → __tests__}/MemoryCacheProvider.spec.ts +1 -1
- package/src/cache/core/primitives/$cache.ts +2 -2
- package/src/cache/redis/providers/RedisCacheProvider.ts +2 -2
- package/src/cli/config/defineConfig.ts +20 -0
- package/src/cli/core/{services → __tests__}/ProjectScaffolder.spec.ts +1 -1
- package/src/cli/core/{commands/gen → __tests__}/changelog.spec.ts +1 -1
- package/src/cli/core/{commands → __tests__}/init.spec.ts +4 -12
- package/src/cli/core/assets.ts +0 -1
- package/src/cli/core/atoms/devOptions.ts +0 -5
- package/src/cli/core/commands/build.ts +2 -2
- package/src/cli/core/commands/dev.ts +165 -30
- package/src/cli/core/commands/gen/changelog.ts +2 -2
- package/src/cli/core/commands/init.ts +2 -7
- package/src/cli/core/commands/verify.ts +0 -1
- package/src/cli/core/providers/AppEntryProvider.ts +2 -2
- package/src/cli/core/providers/ViteDevServerProvider.ts +53 -46
- package/src/cli/core/services/PackageManagerUtils.ts +8 -3
- package/src/cli/core/services/ProjectScaffolder.ts +20 -20
- package/src/cli/core/tasks/BuildClientTask.ts +8 -0
- package/src/cli/core/tasks/BuildServerTask.ts +17 -4
- package/src/cli/core/templates/alephaConfigTs.ts +0 -6
- package/src/cli/core/templates/webAdminDashboardTsx.ts +17 -0
- package/src/cli/core/templates/webAppRouterTs.ts +85 -2
- package/src/cli/devtools/atoms/devtoolsOptions.ts +26 -0
- package/src/cli/devtools/index.ts +194 -0
- package/src/cli/platform/{adapters → __tests__}/CloudflareAdapter.spec.ts +2 -2
- package/src/cli/platform/{providers → __tests__}/GitHubSecretStore.spec.ts +1 -1
- package/src/cli/platform/{services → __tests__}/NamingService.spec.ts +1 -1
- package/src/cli/platform/{providers → __tests__}/PlatformCacheProvider.spec.ts +1 -1
- package/src/cli/platform/{services → __tests__}/PlatformInspector.spec.ts +1 -1
- package/src/cli/platform/{services → __tests__}/PlatformOrchestrator.spec.ts +3 -3
- package/src/cli/platform/{services → __tests__}/SecretFilterService.spec.ts +1 -1
- package/src/cli/platform/{commands → __tests__}/SecretsCommand.spec.ts +1 -1
- package/src/cli/platform/{adapters → __tests__}/VercelAdapter.spec.ts +2 -2
- package/src/cli/platform/atoms/platformOptions.ts +2 -10
- package/src/cli/platform/commands/SecretsCommand.ts +2 -2
- package/src/cli/platform/commands/platform.ts +2 -11
- package/src/cli/platform/index.ts +34 -11
- package/src/cli/platform/services/PlatformInspector.ts +2 -2
- package/src/cli/platform/services/PlatformOrchestrator.ts +0 -9
- package/src/cli/vendor/__tests__/VendorService.spec.ts +407 -0
- package/src/cli/vendor/atoms/vendorOptions.ts +41 -0
- package/src/cli/vendor/commands/VendorCommand.ts +204 -0
- package/src/cli/vendor/index.ts +43 -0
- package/src/cli/vendor/services/VendorService.ts +338 -0
- package/src/command/{providers → __tests__}/CliProvider.spec.ts +1 -1
- package/src/command/{helpers → __tests__}/EnvUtils.spec.ts +1 -1
- package/src/command/providers/CliProvider.ts +2 -2
- package/src/core/{primitives → __tests__}/$atom.spec.ts +2 -2
- package/src/core/{primitives → __tests__}/$memoize.spec.ts +1 -1
- package/src/core/{primitives → __tests__}/$mode.spec.ts +1 -1
- package/src/core/{primitives → __tests__}/$pipeline.spec.ts +1 -1
- package/src/core/{primitives → __tests__}/$scope.spec.ts +2 -2
- package/src/core/{providers → __tests__}/KeylessJsonSchemaCodec.spec.ts +1 -1
- package/src/core/{providers → __tests__}/SchemaValidator.spec.ts +1 -1
- package/src/core/{helpers → __tests__}/jsonSchemaToTypeBox.spec.ts +1 -1
- package/src/core/index.shared.ts +1 -1
- package/src/core/primitives/{$use.ts → $state.ts} +4 -4
- package/src/crypto/{providers → __tests__}/BrowserCryptoProvider.browser.spec.ts +1 -1
- package/src/crypto/{providers → __tests__}/CryptoProvider.spec.ts +1 -1
- package/src/datetime/{primitives → __tests__}/$debounce.spec.ts +1 -1
- package/src/datetime/{primitives → __tests__}/$throttle.spec.ts +1 -1
- package/src/datetime/{primitives → __tests__}/$timeout.spec.ts +1 -1
- package/src/email/brevo/{providers → __tests__}/BrevoEmailProvider.spec.ts +1 -1
- package/src/email/core/{providers → __tests__}/LocalEmailProvider.spec.ts +39 -150
- package/src/email/core/providers/LocalEmailProvider.ts +13 -51
- package/src/email/smtp/providers/NodemailerEmailProvider.ts +2 -2
- package/src/lock/core/{primitives → __tests__}/$lock.middleware.spec.ts +1 -1
- package/src/lock/core/primitives/$lock.ts +2 -2
- package/src/logger/index.ts +10 -4
- package/src/mcp/transports/SseMcpTransport.ts +2 -2
- package/src/orm/__tests__/ModelBuilder-tests.ts +53 -0
- package/src/orm/__tests__/ModelBuilder.spec.ts +80 -0
- package/src/orm/__tests__/organization-tests.ts +200 -0
- package/src/orm/__tests__/organization.spec.ts +103 -0
- package/src/orm/core/{providers/drivers → __tests__}/BunSqliteProvider.bun.spec.ts +5 -2
- package/src/orm/core/constants/PG_SYMBOLS.ts +2 -0
- package/src/orm/core/index.shared.ts +1 -0
- package/src/orm/core/primitives/$entity.ts +31 -0
- package/src/orm/core/providers/DatabaseTypeProvider.ts +11 -0
- package/src/orm/core/providers/DrizzleKitProvider.ts +1 -1
- package/src/orm/core/providers/drivers/BunSqliteProvider.ts +2 -2
- package/src/orm/core/providers/drivers/NodeSqliteProvider.ts +3 -3
- package/src/orm/core/services/ModelBuilder.ts +11 -0
- package/src/orm/core/services/QueryManager.ts +16 -2
- package/src/orm/core/services/Repository.ts +70 -10
- package/src/orm/postgres/{providers → __tests__}/BunPostgresProvider.bun.spec.ts +1 -1
- package/src/orm/postgres/services/PostgresModelBuilder.ts +9 -1
- package/src/queue/core/providers/WorkerProvider.ts +2 -2
- package/src/queue/redis/providers/RedisQueueProvider.ts +2 -2
- package/src/react/core/{hooks → __tests__}/useAction.browser.spec.tsx +1 -1
- package/src/react/core/hooks/useAction.ts +7 -6
- package/src/react/head/{providers → __tests__}/BrowserHeadProvider.browser.spec.ts +1 -1
- package/src/react/head/{helpers → __tests__}/SeoExpander.spec.ts +1 -1
- package/src/react/i18n/{providers → __tests__}/I18nProvider.spec.ts +1 -1
- package/src/react/i18n/{hooks → __tests__}/useI18n.browser.spec.tsx +1 -1
- package/src/react/intro/components/GettingStartedDevtoolsSlide.tsx +3 -6
- package/src/react/router/{providers → __tests__}/ReactBrowserProvider.browser.spec.ts +1 -1
- package/src/react/router/providers/ReactBrowserProvider.ts +2 -2
- package/src/react/router/providers/ReactPageProvider.ts +2 -2
- package/src/react/router/providers/ReactServerProvider.ts +3 -3
- package/src/redis/{providers → __tests__}/BunRedisProvider.bun.spec.ts +4 -4
- package/src/retry/{primitives → __tests__}/$retry.middleware.spec.ts +1 -1
- package/src/router/{TemplatedPathParser.spec.ts → __tests__/TemplatedPathParser.spec.ts} +1 -1
- package/src/scheduler/primitives/$scheduler.ts +2 -2
- package/src/security/{primitives → __tests__}/$secure-browser.spec.ts +1 -1
- package/src/security/{primitives → __tests__}/$secure.spec.ts +1 -1
- package/src/security/primitives/$issuer.ts +1 -1
- package/src/security/providers/JwtProvider.ts +6 -10
- package/src/security/providers/SecurityProvider.ts +6 -11
- package/src/security/schemas/userAccountInfoSchema.ts +3 -3
- package/src/server/auth/providers/ServerAuthProvider.ts +24 -2
- package/src/server/cookies/{services → __tests__}/CookieParser.spec.ts +1 -1
- package/src/server/core/{primitives → __tests__}/$circuit.spec.ts +1 -1
- package/src/server/core/{providers → __tests__}/NodeHttpServerProvider.spec.ts +1 -1
- package/src/server/core/{providers → __tests__}/ServerBodyParserProvider.spec.ts +31 -1
- package/src/server/core/{providers → __tests__}/ServerCompressProvider.spec.ts +1 -1
- package/src/server/core/{providers → __tests__}/ServerHelmetProvider.spec.ts +4 -1
- package/src/server/core/{providers → __tests__}/ServerMultipartProvider.spec.ts +1 -1
- package/src/server/core/{services → __tests__}/ServerRequestParser.spec.ts +1 -1
- package/src/server/core/primitives/$action.ts +2 -2
- package/src/server/core/primitives/$sse.ts +2 -2
- package/src/server/core/providers/ServerBodyParserProvider.ts +21 -12
- package/src/server/core/providers/ServerCompressProvider.ts +2 -2
- package/src/server/core/providers/ServerHelmetProvider.ts +2 -2
- package/src/server/core/providers/ServerMultipartProvider.ts +2 -2
- package/src/server/core/providers/ServerRouterProvider.ts +1 -5
- package/src/server/cors/{primitives → __tests__}/$cors.spec.ts +1 -1
- package/src/server/cors/providers/ServerCorsProvider.ts +2 -2
- package/src/server/links/{services → __tests__}/BatchCollector.spec.ts +1 -1
- package/src/server/links/providers/LinkProvider.ts +2 -2
- package/src/server/links/providers/RemotePrimitiveProvider.ts +2 -2
- package/src/server/links/providers/ServerLinksProvider.ts +2 -2
- package/src/server/rate-limit/{primitives → __tests__}/$rateLimit.spec.ts +1 -1
- package/src/server/rate-limit/providers/ServerRateLimitProvider.ts +2 -2
- package/src/server/swagger/providers/ServerSwaggerProvider.ts +2 -2
- package/src/sms/{providers → __tests__}/LocalSmsProvider.spec.ts +35 -29
- package/src/sms/providers/LocalSmsProvider.ts +13 -24
- package/src/system/{providers → __tests__}/MemoryFileSystemProvider.spec.ts +1 -1
- package/src/system/{providers → __tests__}/MemoryShellProvider.spec.ts +1 -1
- package/src/topic/redis/providers/RedisTopicProvider.ts +2 -2
- package/src/websocket/{services → __tests__}/RoomManager.spec.ts +1 -1
- package/src/websocket/providers/NodeWebSocketServerProvider.ts +2 -2
- package/tsconfig.base.json +1 -0
- package/assets/devtools-ui/200.html +0 -10
- package/assets/devtools-ui/200.html.br +0 -0
- package/assets/devtools-ui/404.html +0 -10
- package/assets/devtools-ui/404.html.br +0 -0
- package/assets/devtools-ui/CNAME +0 -1
- package/assets/devtools-ui/asset.CHpVij2M.css +0 -1
- package/assets/devtools-ui/asset.CHpVij2M.css.br +0 -0
- package/assets/devtools-ui/asset.DJ-i0UDz.css +0 -1
- package/assets/devtools-ui/asset.DJ-i0UDz.css.br +0 -0
- package/assets/devtools-ui/chunk.1jwpJORo.js +0 -1
- package/assets/devtools-ui/chunk.1jwpJORo.js.br +0 -0
- package/assets/devtools-ui/chunk.B0r2wfUL.js +0 -1
- package/assets/devtools-ui/chunk.B0r2wfUL.js.br +0 -0
- package/assets/devtools-ui/chunk.BScN4dVR.js +0 -84
- package/assets/devtools-ui/chunk.BScN4dVR.js.br +0 -0
- package/assets/devtools-ui/chunk.BispuoY4.js +0 -1
- package/assets/devtools-ui/chunk.BispuoY4.js.br +0 -0
- package/assets/devtools-ui/chunk.BtrPUUd7.js +0 -2
- package/assets/devtools-ui/chunk.BtrPUUd7.js.br +0 -0
- package/assets/devtools-ui/chunk.C-KMHgqf.js +0 -1
- package/assets/devtools-ui/chunk.C-KMHgqf.js.br +0 -0
- package/assets/devtools-ui/chunk.C2zQ3CF6.js +0 -7
- package/assets/devtools-ui/chunk.C2zQ3CF6.js.br +0 -0
- package/assets/devtools-ui/chunk.C619McpO.js +0 -1
- package/assets/devtools-ui/chunk.C619McpO.js.br +0 -0
- package/assets/devtools-ui/chunk.C9OsYsVl.js +0 -1
- package/assets/devtools-ui/chunk.C9OsYsVl.js.br +0 -3
- package/assets/devtools-ui/chunk.CAC_-151.js +0 -1
- package/assets/devtools-ui/chunk.CAC_-151.js.br +0 -0
- package/assets/devtools-ui/chunk.CCmesWfx.js +0 -1
- package/assets/devtools-ui/chunk.CCmesWfx.js.br +0 -0
- package/assets/devtools-ui/chunk.CFQDmrcV.js +0 -1
- package/assets/devtools-ui/chunk.CFQDmrcV.js.br +0 -0
- package/assets/devtools-ui/chunk.CgM71Zau.js +0 -1
- package/assets/devtools-ui/chunk.CgM71Zau.js.br +0 -0
- package/assets/devtools-ui/chunk.CmHq336a.js +0 -1
- package/assets/devtools-ui/chunk.CmHq336a.js.br +0 -0
- package/assets/devtools-ui/chunk.CthNMCar.js +0 -1
- package/assets/devtools-ui/chunk.CthNMCar.js.br +0 -0
- package/assets/devtools-ui/chunk.D2brVJP1.js +0 -1
- package/assets/devtools-ui/chunk.D2brVJP1.js.br +0 -0
- package/assets/devtools-ui/chunk.DgBKGkiw.js +0 -1
- package/assets/devtools-ui/chunk.DgBKGkiw.js.br +0 -0
- package/assets/devtools-ui/chunk.NNHHCQsa.js +0 -1
- package/assets/devtools-ui/chunk.NNHHCQsa.js.br +0 -0
- package/assets/devtools-ui/chunk._CDKKWMo.js +0 -1
- package/assets/devtools-ui/chunk._CDKKWMo.js.br +0 -0
- package/assets/devtools-ui/entry.B_nOjAod.js +0 -2
- package/assets/devtools-ui/entry.B_nOjAod.js.br +0 -0
- package/assets/devtools-ui/index.html +0 -10
- package/assets/devtools-ui/index.html.br +0 -0
- package/dist/devtools/index.browser.js +0 -224
- package/dist/devtools/index.browser.js.map +0 -1
- package/dist/devtools/index.d.ts +0 -499
- package/dist/devtools/index.d.ts.map +0 -1
- package/dist/devtools/index.js +0 -782
- package/dist/devtools/index.js.map +0 -1
- package/dist/mqtt/index.d.ts +0 -164
- package/dist/mqtt/index.d.ts.map +0 -1
- package/dist/mqtt/index.js +0 -214
- package/dist/mqtt/index.js.map +0 -1
- package/dist/topic/mqtt/index.d.ts +0 -87
- package/dist/topic/mqtt/index.d.ts.map +0 -1
- package/dist/topic/mqtt/index.js +0 -88
- package/dist/topic/mqtt/index.js.map +0 -1
- package/src/api/users/parameters/UserParameters.ts +0 -23
- package/src/api/users/services/SessionService.spec.ts +0 -303
- package/src/cli/platform/adapters/DockerAdapter.spec.ts +0 -378
- package/src/cli/platform/adapters/DockerAdapter.ts +0 -417
- package/src/cli/platform/services/DockerComposeGenerator.spec.ts +0 -490
- package/src/cli/platform/services/DockerComposeGenerator.ts +0 -353
- package/src/cli/platform/services/DockerSshService.spec.ts +0 -47
- package/src/cli/platform/services/DockerSshService.ts +0 -61
- package/src/devtools/__tests__/DevCollectorProvider.spec.ts +0 -7
- package/src/devtools/assets.ts +0 -6
- package/src/devtools/entities/logs.ts +0 -21
- package/src/devtools/index.browser.ts +0 -11
- package/src/devtools/index.shared.ts +0 -15
- package/src/devtools/index.ts +0 -39
- package/src/devtools/providers/DevToolsMetadataProvider.ts +0 -459
- package/src/devtools/providers/DevToolsProvider.ts +0 -280
- package/src/devtools/schemas/DevActionMetadata.ts +0 -30
- package/src/devtools/schemas/DevAtomMetadata.ts +0 -26
- package/src/devtools/schemas/DevBucketMetadata.ts +0 -11
- package/src/devtools/schemas/DevCacheMetadata.ts +0 -10
- package/src/devtools/schemas/DevEntityMetadata.ts +0 -60
- package/src/devtools/schemas/DevEnvMetadata.ts +0 -22
- package/src/devtools/schemas/DevMetadata.ts +0 -47
- package/src/devtools/schemas/DevModuleMetadata.ts +0 -8
- package/src/devtools/schemas/DevPageMetadata.ts +0 -24
- package/src/devtools/schemas/DevProviderMetadata.ts +0 -10
- package/src/devtools/schemas/DevQueueMetadata.ts +0 -10
- package/src/devtools/schemas/DevRealmMetadata.ts +0 -19
- package/src/devtools/schemas/DevRouteMetadata.ts +0 -8
- package/src/devtools/schemas/DevSchedulerMetadata.ts +0 -11
- package/src/devtools/schemas/DevTopicMetadata.ts +0 -11
- package/src/mqtt/index.ts +0 -32
- package/src/mqtt/providers/MqttClientProvider.ts +0 -63
- package/src/mqtt/providers/MqttJsClientProvider.spec.ts +0 -165
- package/src/mqtt/providers/MqttJsClientProvider.ts +0 -309
- package/src/topic/mqtt/index.ts +0 -30
- package/src/topic/mqtt/providers/MqttTopicProvider.spec.ts +0 -160
- package/src/topic/mqtt/providers/MqttTopicProvider.ts +0 -146
- /package/src/api/audits/{primitives → __tests__}/$audit.spec.ts +0 -0
- /package/src/api/audits/{services → __tests__}/AuditService.spec.ts +0 -0
- /package/src/api/files/{controllers → __tests__}/AdminFileStatsController.spec.ts +0 -0
- /package/src/api/files/{jobs → __tests__}/FileJobs.spec.ts +0 -0
- /package/src/api/jobs/{primitives → __tests__}/$job-middleware.spec.ts +0 -0
- /package/src/api/parameters/{primitives → __tests__}/$parameter.spec.ts +0 -0
- /package/src/api/users/{controllers → __tests__}/AdminIdentityController.spec.ts +0 -0
- /package/src/api/users/{controllers → __tests__}/AdminSessionController.spec.ts +0 -0
- /package/src/api/users/{controllers → __tests__}/AdminUserController.spec.ts +0 -0
- /package/src/api/users/{services → __tests__}/CredentialService.spec.ts +0 -0
- /package/src/api/users/{services → __tests__}/RegistrationService.spec.ts +0 -0
- /package/src/batch/{primitives → __tests__}/$batch.spec.ts +0 -0
- /package/src/batch/{providers → __tests__}/BatchProvider.spec.ts +0 -0
- /package/src/bucket/{primitives → __tests__}/$bucket.spec.ts +0 -0
- /package/src/bucket/{providers → __tests__}/FileStorageProvider.spec.ts +0 -0
- /package/src/bucket/{providers → __tests__}/LocalFileStorageProvider.spec.ts +0 -0
- /package/src/bucket/{providers → __tests__}/MemoryFileStorageProvider.spec.ts +0 -0
- /package/src/cache/core/{primitives → __tests__}/$cache.spec.ts +0 -0
- /package/src/cache/redis/{providers → __tests__}/RedisCacheProvider.spec.ts +0 -0
- /package/src/command/{primitives → __tests__}/$command.spec.ts +0 -0
- /package/src/command/{helpers → __tests__}/Asker.spec.ts +0 -0
- /package/src/command/{helpers → __tests__}/Runner.spec.ts +0 -0
- /package/src/core/{primitives → __tests__}/$context.spec.ts +0 -0
- /package/src/core/{primitives → __tests__}/$env.spec.ts +0 -0
- /package/src/core/{primitives → __tests__}/$hook.spec.ts +0 -0
- /package/src/core/{primitives → __tests__}/$inject.spec.ts +0 -0
- /package/src/core/{primitives → __tests__}/$module.spec.ts +0 -0
- /package/src/core/{providers → __tests__}/CodecManager.spec.ts +0 -0
- /package/src/core/{providers → __tests__}/EventManager.spec.ts +0 -0
- /package/src/core/{providers → __tests__}/StateManager.spec.ts +0 -0
- /package/src/core/{providers → __tests__}/TypeProvider.spec.ts +0 -0
- /package/src/datetime/{primitives → __tests__}/$interval.spec.ts +0 -0
- /package/src/datetime/{providers → __tests__}/DateTimeProvider.spec.ts +0 -0
- /package/src/email/core/{primitives → __tests__}/$email.spec.ts +0 -0
- /package/src/fake/{providers → __tests__}/FakeProvider.spec.ts +0 -0
- /package/src/lock/core/{providers → __tests__}/MemoryLockProvider.spec.ts +0 -0
- /package/src/lock/redis/{providers → __tests__}/RedisLockProvider.spec.ts +0 -0
- /package/src/logger/{primitives → __tests__}/$logger.spec.ts +0 -0
- /package/src/logger/{services → __tests__}/Logger.spec.ts +0 -0
- /package/src/mcp/{primitives → __tests__}/$prompt.spec.ts +0 -0
- /package/src/mcp/{primitives → __tests__}/$resource.spec.ts +0 -0
- /package/src/mcp/{primitives → __tests__}/$tool.spec.ts +0 -0
- /package/src/mcp/{providers → __tests__}/McpServerProvider.spec.ts +0 -0
- /package/src/mcp/{helpers → __tests__}/jsonrpc.spec.ts +0 -0
- /package/src/orm/core/{helpers → __tests__}/parseQueryString.spec.ts +0 -0
- /package/src/queue/core/{primitives → __tests__}/$consumer.spec.ts +0 -0
- /package/src/queue/core/{providers → __tests__}/MemoryQueueProvider.spec.ts +0 -0
- /package/src/queue/core/{providers → __tests__}/WorkerProvider.spec.ts +0 -0
- /package/src/queue/redis/{providers → __tests__}/RedisQueueProvider.spec.ts +0 -0
- /package/src/react/form/{hooks → __tests__}/useForm.browser.spec.tsx +0 -0
- /package/src/react/head/{hooks → __tests__}/useHead.spec.tsx +0 -0
- /package/src/react/i18n/{components → __tests__}/Localize.spec.tsx +0 -0
- /package/src/react/router/{primitives → __tests__}/$page.browser.spec.tsx +0 -0
- /package/src/react/router/{primitives → __tests__}/$page.middleware.spec.tsx +0 -0
- /package/src/react/router/{primitives → __tests__}/$page.spec.tsx +0 -0
- /package/src/react/router/{providers → __tests__}/ReactPreloadProvider.spec.ts +0 -0
- /package/src/react/router/{providers → __tests__}/ReactServerProvider.spec.tsx +0 -0
- /package/src/react/router/{providers → __tests__}/ReactServerTemplateProvider.spec.ts +0 -0
- /package/src/retry/{primitives → __tests__}/$retry.spec.ts +0 -0
- /package/src/retry/{providers → __tests__}/RetryProvider.spec.ts +0 -0
- /package/src/router/{providers → __tests__}/RouterProvider.spec.ts +0 -0
- /package/src/security/{primitives → __tests__}/$issuer.spec.ts +0 -0
- /package/src/security/{primitives → __tests__}/$permission.spec.ts +0 -0
- /package/src/security/{primitives → __tests__}/$role.spec.ts +0 -0
- /package/src/security/{primitives → __tests__}/$serviceAccount.spec.ts +0 -0
- /package/src/security/{providers → __tests__}/SecurityProvider.spec.ts +0 -0
- /package/src/server/cookies/{providers → __tests__}/ServerCookiesProvider.spec.ts +0 -0
- /package/src/server/core/{primitives → __tests__}/$action.spec.ts +0 -0
- /package/src/server/core/{primitives → __tests__}/$middleware.spec.ts +0 -0
- /package/src/server/core/{primitives → __tests__}/$route.spec.ts +0 -0
- /package/src/server/core/{primitives → __tests__}/$sse.spec.ts +0 -0
- /package/src/server/core/{providers → __tests__}/BunHttpServerProvider.bun.spec.ts +0 -0
- /package/src/server/core/{services → __tests__}/HttpClient.spec.ts +0 -0
- /package/src/server/core/{providers → __tests__}/ServerLoggerProvider.spec.ts +0 -0
- /package/src/server/core/{services → __tests__}/UserAgentParser.spec.ts +0 -0
- /package/src/server/cors/{providers → __tests__}/ServerCorsProvider.spec.ts +0 -0
- /package/src/server/etag/{providers → __tests__}/ServerEtagProvider.spec.ts +0 -0
- /package/src/server/health/{providers → __tests__}/ServerHealthProvider.spec.ts +0 -0
- /package/src/server/links/{primitives → __tests__}/$remote.spec.ts +0 -0
- /package/src/server/links/{services → __tests__}/BatchEndpoint.spec.ts +0 -0
- /package/src/server/links/{providers → __tests__}/LinkProvider.spec.ts +0 -0
- /package/src/server/links/{providers → __tests__}/ServerLinksProvider.spec.ts +0 -0
- /package/src/server/metrics/{providers → __tests__}/ServerMetricsProvider.spec.ts +0 -0
- /package/src/server/proxy/{primitives → __tests__}/$proxy.spec.ts +0 -0
- /package/src/server/rate-limit/{providers → __tests__}/ServerRateLimitProvider.spec.ts +0 -0
- /package/src/server/static/{primitives → __tests__}/$serve.spec.ts +0 -0
- /package/src/server/swagger/{primitives → __tests__}/$swagger.spec.ts +0 -0
- /package/src/sms/{primitives → __tests__}/$sms.spec.ts +0 -0
- /package/src/sms/{providers → __tests__}/MemorySmsProvider.spec.ts +0 -0
- /package/src/system/{services → __tests__}/FileDetector.spec.ts +0 -0
- /package/src/system/{providers → __tests__}/NodeFileSystemProvider.spec.ts +0 -0
- /package/src/topic/core/{primitives → __tests__}/$subscriber.spec.ts +0 -0
- /package/src/topic/core/{providers → __tests__}/MemoryTopicProvider.spec.ts +0 -0
- /package/src/topic/redis/{providers → __tests__}/RedisTopicProvider.spec.ts +0 -0
- /package/src/websocket/{primitives → __tests__}/$channel.spec.ts +0 -0
|
@@ -5,7 +5,7 @@ import { AlephaOrmPostgres } from "alepha/orm/postgres";
|
|
|
5
5
|
import { describe, test, vi } from "vitest";
|
|
6
6
|
import { sessions } from "../entities/sessions.ts";
|
|
7
7
|
import { users } from "../entities/users.ts";
|
|
8
|
-
import { UserJobs } from "
|
|
8
|
+
import { UserJobs } from "../jobs/UserJobs.ts";
|
|
9
9
|
|
|
10
10
|
describe("UserJobs", () => {
|
|
11
11
|
describe("purgeExpiredSessions", () => {
|
|
@@ -89,6 +89,25 @@ export const realmAuthSettingsAtom = $atom({
|
|
|
89
89
|
description: "Require at least one special character",
|
|
90
90
|
}),
|
|
91
91
|
}),
|
|
92
|
+
loginRateLimit: t.object({
|
|
93
|
+
ipMaxAttempts: t.integer({
|
|
94
|
+
description:
|
|
95
|
+
"Max failed login attempts per IP before temporary lockout",
|
|
96
|
+
default: 15,
|
|
97
|
+
minimum: 1,
|
|
98
|
+
}),
|
|
99
|
+
accountMaxAttempts: t.integer({
|
|
100
|
+
description:
|
|
101
|
+
"Max failed login attempts per account before temporary lockout",
|
|
102
|
+
default: 5,
|
|
103
|
+
minimum: 1,
|
|
104
|
+
}),
|
|
105
|
+
windowMs: t.integer({
|
|
106
|
+
description: "Rate limit window duration in milliseconds",
|
|
107
|
+
default: 15 * 60 * 1000,
|
|
108
|
+
minimum: 1000,
|
|
109
|
+
}),
|
|
110
|
+
}),
|
|
92
111
|
}),
|
|
93
112
|
default: {
|
|
94
113
|
// for a fresh hello world setup, we accept registration and email login
|
|
@@ -111,6 +130,11 @@ export const realmAuthSettingsAtom = $atom({
|
|
|
111
130
|
requireNumbers: true,
|
|
112
131
|
requireSpecialCharacters: false,
|
|
113
132
|
},
|
|
133
|
+
loginRateLimit: {
|
|
134
|
+
ipMaxAttempts: 15,
|
|
135
|
+
accountMaxAttempts: 5,
|
|
136
|
+
windowMs: 15 * 60 * 1000,
|
|
137
|
+
},
|
|
114
138
|
},
|
|
115
139
|
});
|
|
116
140
|
|
|
@@ -23,23 +23,17 @@ export class RealmController {
|
|
|
23
23
|
group: this.group,
|
|
24
24
|
method: "GET",
|
|
25
25
|
path: `${this.url}/config`,
|
|
26
|
-
use: [
|
|
27
|
-
$etag({
|
|
28
|
-
control: {
|
|
29
|
-
maxAge: [24, "hours"],
|
|
30
|
-
},
|
|
31
|
-
}),
|
|
32
|
-
],
|
|
26
|
+
use: [$etag()],
|
|
33
27
|
schema: {
|
|
34
28
|
query: t.object({
|
|
35
29
|
realmName: t.optional(t.string()),
|
|
36
30
|
}),
|
|
37
31
|
response: realmConfigSchema,
|
|
38
32
|
},
|
|
39
|
-
handler: ({ query }) => {
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
33
|
+
handler: async ({ query }) => {
|
|
34
|
+
const realm = this.realmProvider.getRealm(query.realmName);
|
|
35
|
+
const settings = await realm.getSettings();
|
|
36
|
+
const realmName = realm.name;
|
|
43
37
|
|
|
44
38
|
const authenticationMethods =
|
|
45
39
|
this.serverAuthProvider.getAuthenticationProviders({
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Static, t } from "alepha";
|
|
2
|
-
import { $entity, db } from "alepha/orm";
|
|
2
|
+
import { $entity, db, sql } from "alepha/orm";
|
|
3
3
|
|
|
4
4
|
export const DEFAULT_USER_REALM_NAME = "default";
|
|
5
5
|
|
|
@@ -16,7 +16,7 @@ export const users = $entity({
|
|
|
16
16
|
username: t.optional(
|
|
17
17
|
t.shortText({
|
|
18
18
|
minLength: 3,
|
|
19
|
-
maxLength:
|
|
19
|
+
maxLength: 30,
|
|
20
20
|
// pattern is handled at the realm settings level
|
|
21
21
|
}),
|
|
22
22
|
),
|
|
@@ -32,9 +32,15 @@ export const users = $entity({
|
|
|
32
32
|
enabled: db.default(t.boolean(), true),
|
|
33
33
|
|
|
34
34
|
emailVerified: db.default(t.boolean(), false),
|
|
35
|
+
|
|
36
|
+
organizationId: db.organization(),
|
|
35
37
|
}),
|
|
36
38
|
indexes: [
|
|
37
|
-
{
|
|
39
|
+
{
|
|
40
|
+
expressions: (self) => [self.realm, sql`LOWER(${self.username})`],
|
|
41
|
+
unique: true,
|
|
42
|
+
name: "users_realm_username_lower_idx",
|
|
43
|
+
},
|
|
38
44
|
{ columns: ["realm", "email"], unique: true },
|
|
39
45
|
{ columns: ["realm", "phoneNumber"], unique: true },
|
|
40
46
|
],
|
package/src/api/users/index.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { $module } from "alepha";
|
|
2
|
-
import {
|
|
2
|
+
import { UserAudits } from "./audits/UserAudits.ts";
|
|
3
|
+
import { UserBuckets } from "./buckets/UserBuckets.ts";
|
|
3
4
|
import { AdminIdentityController } from "./controllers/AdminIdentityController.ts";
|
|
4
5
|
import { AdminSessionController } from "./controllers/AdminSessionController.ts";
|
|
5
6
|
import { AdminUserController } from "./controllers/AdminUserController.ts";
|
|
6
7
|
import { RealmController } from "./controllers/RealmController.ts";
|
|
7
8
|
import { UserController } from "./controllers/UserController.ts";
|
|
9
|
+
import { UserJobs } from "./jobs/UserJobs.ts";
|
|
10
|
+
import { UserNotifications } from "./notifications/UserNotifications.ts";
|
|
8
11
|
import { RealmProvider } from "./providers/RealmProvider.ts";
|
|
9
12
|
import { CredentialService } from "./services/CredentialService.ts";
|
|
10
13
|
import { IdentityService } from "./services/IdentityService.ts";
|
|
@@ -28,7 +31,6 @@ export * from "./entities/sessions.ts";
|
|
|
28
31
|
export * from "./entities/users.ts";
|
|
29
32
|
export * from "./jobs/UserJobs.ts";
|
|
30
33
|
export * from "./notifications/UserNotifications.ts";
|
|
31
|
-
export * from "./parameters/UserParameters.ts";
|
|
32
34
|
export * from "./primitives/$realm.ts";
|
|
33
35
|
export * from "./providers/RealmProvider.ts";
|
|
34
36
|
export * from "./schemas/completePasswordResetRequestSchema.ts";
|
|
@@ -74,7 +76,6 @@ export * from "./services/UserService.ts";
|
|
|
74
76
|
export const AlephaApiUsers = $module({
|
|
75
77
|
name: "alepha.api.users",
|
|
76
78
|
services: [
|
|
77
|
-
AlephaEmail,
|
|
78
79
|
RealmProvider,
|
|
79
80
|
SessionService,
|
|
80
81
|
SessionCrudService,
|
|
@@ -87,5 +88,24 @@ export const AlephaApiUsers = $module({
|
|
|
87
88
|
AdminSessionController,
|
|
88
89
|
AdminIdentityController,
|
|
89
90
|
RealmController,
|
|
91
|
+
UserJobs,
|
|
92
|
+
UserNotifications,
|
|
93
|
+
UserAudits,
|
|
94
|
+
UserBuckets,
|
|
90
95
|
],
|
|
96
|
+
register: (alepha) => {
|
|
97
|
+
alepha
|
|
98
|
+
.with(RealmProvider)
|
|
99
|
+
.with(SessionService)
|
|
100
|
+
.with(SessionCrudService)
|
|
101
|
+
.with(CredentialService)
|
|
102
|
+
.with(RegistrationService)
|
|
103
|
+
.with(UserService)
|
|
104
|
+
.with(IdentityService)
|
|
105
|
+
.with(UserController)
|
|
106
|
+
.with(AdminUserController)
|
|
107
|
+
.with(AdminSessionController)
|
|
108
|
+
.with(AdminIdentityController)
|
|
109
|
+
.with(RealmController);
|
|
110
|
+
},
|
|
91
111
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { $context } from "alepha";
|
|
2
2
|
import { AlephaApiKeys, ApiKeyService } from "alepha/api/keys";
|
|
3
|
-
import { AlephaApiParameters } from "alepha/api/parameters";
|
|
3
|
+
import { $parameter, AlephaApiParameters } from "alepha/api/parameters";
|
|
4
4
|
import { AlephaApiVerification } from "alepha/api/verifications";
|
|
5
5
|
import type { Repository } from "alepha/orm";
|
|
6
6
|
import {
|
|
@@ -21,7 +21,10 @@ import {
|
|
|
21
21
|
type WithLinkFn,
|
|
22
22
|
type WithLoginFn,
|
|
23
23
|
} from "alepha/server/auth";
|
|
24
|
-
import
|
|
24
|
+
import {
|
|
25
|
+
type RealmAuthSettings,
|
|
26
|
+
realmAuthSettingsAtom,
|
|
27
|
+
} from "../atoms/realmAuthSettingsAtom.ts";
|
|
25
28
|
import { UserAudits } from "../audits/UserAudits.ts";
|
|
26
29
|
import { UserBuckets } from "../buckets/UserBuckets.ts";
|
|
27
30
|
import type { identities } from "../entities/identities.ts";
|
|
@@ -60,7 +63,7 @@ export const $realm = (options: RealmOptions = {}): RealmPrimitive => {
|
|
|
60
63
|
|
|
61
64
|
// Merge features with defaults
|
|
62
65
|
const features: RealmFeatures = {
|
|
63
|
-
|
|
66
|
+
jobs: false,
|
|
64
67
|
notifications: false,
|
|
65
68
|
apiKeys: false,
|
|
66
69
|
parameters: false,
|
|
@@ -92,7 +95,7 @@ export const $realm = (options: RealmOptions = {}): RealmPrimitive => {
|
|
|
92
95
|
alepha.with(UserAudits);
|
|
93
96
|
}
|
|
94
97
|
|
|
95
|
-
if (features.
|
|
98
|
+
if (features.jobs) {
|
|
96
99
|
alepha.with(UserJobs);
|
|
97
100
|
}
|
|
98
101
|
|
|
@@ -101,11 +104,6 @@ export const $realm = (options: RealmOptions = {}): RealmPrimitive => {
|
|
|
101
104
|
alepha.with(AlephaApiVerification);
|
|
102
105
|
}
|
|
103
106
|
|
|
104
|
-
if (features.parameters) {
|
|
105
|
-
// for now, we don't have $parameter related to users, we just register the module
|
|
106
|
-
alepha.with(AlephaApiParameters);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
107
|
// -------------------------------------------------------------------------------------------------------------------
|
|
110
108
|
|
|
111
109
|
// Collect custom resolvers that will be registered during $issuer.onInit()
|
|
@@ -210,6 +208,18 @@ export const $realm = (options: RealmOptions = {}): RealmPrimitive => {
|
|
|
210
208
|
alepha.with(() => auth);
|
|
211
209
|
}
|
|
212
210
|
|
|
211
|
+
if (features.parameters) {
|
|
212
|
+
alepha.with(AlephaApiParameters);
|
|
213
|
+
const settingsParam = $parameter({
|
|
214
|
+
name: `api.realms.settings.${name}`,
|
|
215
|
+
description: `Authentication and registration settings for realm "${name}"`,
|
|
216
|
+
schema: realmAuthSettingsAtom.schema,
|
|
217
|
+
default: realmRegistration.settings,
|
|
218
|
+
});
|
|
219
|
+
realmRegistration.settingsParameter = settingsParam;
|
|
220
|
+
alepha.with(() => ({ [`realmSettings_${name}`]: settingsParam }));
|
|
221
|
+
}
|
|
222
|
+
|
|
213
223
|
return realm;
|
|
214
224
|
};
|
|
215
225
|
|
|
@@ -217,11 +227,13 @@ export const $realm = (options: RealmOptions = {}): RealmPrimitive => {
|
|
|
217
227
|
|
|
218
228
|
export interface RealmFeatures {
|
|
219
229
|
/**
|
|
220
|
-
*
|
|
230
|
+
* Will enable Job module.
|
|
231
|
+
*
|
|
232
|
+
* - Enable session purge functionality for cleaning up expired sessions.
|
|
221
233
|
*
|
|
222
234
|
* @default false
|
|
223
235
|
*/
|
|
224
|
-
|
|
236
|
+
jobs?: boolean;
|
|
225
237
|
|
|
226
238
|
/**
|
|
227
239
|
* Enable notification system for password reset, verification emails, etc.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { $inject, Alepha, AlephaError } from "alepha";
|
|
2
|
+
import type { ParameterPrimitive } from "alepha/api/parameters";
|
|
2
3
|
import { $repository, type Repository } from "alepha/orm";
|
|
3
4
|
import {
|
|
4
5
|
type RealmAuthSettings,
|
|
@@ -20,6 +21,8 @@ export interface Realm {
|
|
|
20
21
|
repositories: RealmRepositories;
|
|
21
22
|
settings: RealmAuthSettings;
|
|
22
23
|
features: RealmFeatures;
|
|
24
|
+
settingsParameter?: ParameterPrimitive<typeof realmAuthSettingsAtom.schema>;
|
|
25
|
+
getSettings(): Promise<RealmAuthSettings>;
|
|
23
26
|
}
|
|
24
27
|
|
|
25
28
|
export class RealmProvider {
|
|
@@ -32,9 +35,15 @@ export class RealmProvider {
|
|
|
32
35
|
protected realms = new Map<string, Realm>();
|
|
33
36
|
|
|
34
37
|
public register(realmName: string, realmOptions: RealmOptions = {}) {
|
|
38
|
+
if (realmName.includes(".")) {
|
|
39
|
+
throw new AlephaError(
|
|
40
|
+
`Realm name "${realmName}" must not contain dots — dots are reserved for parameter tree paths`,
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
35
44
|
// Merge features with defaults
|
|
36
45
|
const features: RealmFeatures = {
|
|
37
|
-
|
|
46
|
+
jobs: false,
|
|
38
47
|
notifications: false,
|
|
39
48
|
apiKeys: false,
|
|
40
49
|
parameters: false,
|
|
@@ -43,7 +52,7 @@ export class RealmProvider {
|
|
|
43
52
|
...realmOptions.features,
|
|
44
53
|
};
|
|
45
54
|
|
|
46
|
-
|
|
55
|
+
const realm: Realm = {
|
|
47
56
|
name: realmName,
|
|
48
57
|
repositories: {
|
|
49
58
|
identities: realmOptions.entities?.identities ?? this.defaultIdentities,
|
|
@@ -58,9 +67,20 @@ export class RealmProvider {
|
|
|
58
67
|
...realmAuthSettingsAtom.options.default.passwordPolicy,
|
|
59
68
|
...realmOptions.settings?.passwordPolicy,
|
|
60
69
|
},
|
|
70
|
+
loginRateLimit: {
|
|
71
|
+
...realmAuthSettingsAtom.options.default.loginRateLimit,
|
|
72
|
+
...realmOptions.settings?.loginRateLimit,
|
|
73
|
+
},
|
|
61
74
|
},
|
|
62
75
|
features,
|
|
63
|
-
|
|
76
|
+
getSettings: async function () {
|
|
77
|
+
if (this.settingsParameter) {
|
|
78
|
+
return await this.settingsParameter.get();
|
|
79
|
+
}
|
|
80
|
+
return this.settings;
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
this.realms.set(realmName, realm);
|
|
64
84
|
return this.getRealm(realmName);
|
|
65
85
|
}
|
|
66
86
|
|
|
@@ -88,7 +88,8 @@ export class RegistrationService {
|
|
|
88
88
|
userRealmName,
|
|
89
89
|
});
|
|
90
90
|
|
|
91
|
-
const
|
|
91
|
+
const realm = this.realmProvider.getRealm(userRealmName);
|
|
92
|
+
const realmSettings = await realm.getSettings();
|
|
92
93
|
|
|
93
94
|
// Check if registration is allowed
|
|
94
95
|
if (realmSettings?.registrationAllowed === false) {
|
|
@@ -338,7 +339,7 @@ export class RegistrationService {
|
|
|
338
339
|
|
|
339
340
|
if (body.username) {
|
|
340
341
|
const existingUser = await userRepository.findOne({
|
|
341
|
-
where: { username: {
|
|
342
|
+
where: { username: { ilike: body.username } },
|
|
342
343
|
});
|
|
343
344
|
if (existingUser) {
|
|
344
345
|
this.log.debug("Username already taken", { username: body.username });
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { randomInt } from "node:crypto";
|
|
2
2
|
import { $inject, Alepha } from "alepha";
|
|
3
3
|
import type { FileController } from "alepha/api/files";
|
|
4
|
+
import { CacheProvider } from "alepha/cache";
|
|
4
5
|
import { DateTimeProvider } from "alepha/datetime";
|
|
5
6
|
import { $logger } from "alepha/logger";
|
|
6
7
|
import {
|
|
@@ -8,7 +9,7 @@ import {
|
|
|
8
9
|
InvalidCredentialsError,
|
|
9
10
|
type UserAccount,
|
|
10
11
|
} from "alepha/security";
|
|
11
|
-
import { UnauthorizedError } from "alepha/server";
|
|
12
|
+
import { BadRequestError, UnauthorizedError } from "alepha/server";
|
|
12
13
|
import type { OAuth2Profile } from "alepha/server/auth";
|
|
13
14
|
import { $client } from "alepha/server/links";
|
|
14
15
|
import { FileSystemProvider } from "alepha/system";
|
|
@@ -24,6 +25,7 @@ export class SessionService {
|
|
|
24
25
|
protected readonly log = $logger();
|
|
25
26
|
protected readonly realmProvider = $inject(RealmProvider);
|
|
26
27
|
protected readonly fileController = $client<FileController>();
|
|
28
|
+
protected readonly cacheProvider = $inject(CacheProvider);
|
|
27
29
|
|
|
28
30
|
protected userAudits(realmName?: string) {
|
|
29
31
|
const realm = this.realmProvider.getRealm(realmName);
|
|
@@ -60,7 +62,9 @@ export class SessionService {
|
|
|
60
62
|
): Promise<boolean> {
|
|
61
63
|
if (user.roles.includes("admin")) return false;
|
|
62
64
|
|
|
63
|
-
const
|
|
65
|
+
const realm = this.realmProvider.getRealm(userRealmName);
|
|
66
|
+
const settings = await realm.getSettings();
|
|
67
|
+
const { name } = realm;
|
|
64
68
|
const adminEmails = settings.adminEmails ?? [];
|
|
65
69
|
const adminUsernames = settings.adminUsernames ?? [];
|
|
66
70
|
|
|
@@ -94,6 +98,78 @@ export class SessionService {
|
|
|
94
98
|
return true;
|
|
95
99
|
}
|
|
96
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Generate a unique username from an OAuth profile.
|
|
103
|
+
*
|
|
104
|
+
* 1. Extract candidate from email prefix
|
|
105
|
+
* 2. Sanitize against realm's usernameRegExp (strip invalid chars, truncate)
|
|
106
|
+
* 3. Check case-insensitive uniqueness, append suffix (2, 3, ...) if taken
|
|
107
|
+
* 4. Fall back to "user" + random 6-char alphanumeric if all else fails
|
|
108
|
+
*/
|
|
109
|
+
protected async generateUniqueUsername(
|
|
110
|
+
profile: OAuth2Profile,
|
|
111
|
+
realmSettings: any,
|
|
112
|
+
users: any,
|
|
113
|
+
): Promise<string> {
|
|
114
|
+
const maxLength = 30;
|
|
115
|
+
const maxSuffixAttempts = 10;
|
|
116
|
+
|
|
117
|
+
// Extract candidate from email or profile name
|
|
118
|
+
let candidate = profile.email?.split("@")[0] ?? profile.name ?? "";
|
|
119
|
+
|
|
120
|
+
// Strip characters not allowed in usernames (keep alphanumeric, underscore, dot, hyphen)
|
|
121
|
+
candidate = candidate.replace(/[^a-zA-Z0-9_.-]/g, "");
|
|
122
|
+
|
|
123
|
+
// If realm has a custom regex, further sanitize
|
|
124
|
+
if (realmSettings?.usernameRegExp) {
|
|
125
|
+
try {
|
|
126
|
+
const regex = new RegExp(realmSettings.usernameRegExp);
|
|
127
|
+
if (!regex.test(candidate)) {
|
|
128
|
+
// Strip to basic alphanumeric as safe fallback
|
|
129
|
+
candidate = candidate.replace(/[^a-zA-Z0-9_]/g, "");
|
|
130
|
+
}
|
|
131
|
+
} catch {
|
|
132
|
+
// Invalid regex, continue with sanitized candidate
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Ensure minimum length
|
|
137
|
+
if (candidate.length < 3) {
|
|
138
|
+
candidate = `user${candidate}`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Truncate to leave room for suffix
|
|
142
|
+
candidate = candidate.slice(0, maxLength - 2);
|
|
143
|
+
|
|
144
|
+
// Check uniqueness (case-insensitive)
|
|
145
|
+
const isAvailable = async (name: string) => {
|
|
146
|
+
const existing = await users.findMany({
|
|
147
|
+
where: { username: { contains: name } },
|
|
148
|
+
limit: 1,
|
|
149
|
+
});
|
|
150
|
+
// Case-insensitive check
|
|
151
|
+
return !existing.some(
|
|
152
|
+
(u: any) => u.username?.toLowerCase() === name.toLowerCase(),
|
|
153
|
+
);
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
if (await isAvailable(candidate)) {
|
|
157
|
+
return candidate;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Try with numeric suffix
|
|
161
|
+
for (let i = 2; i <= maxSuffixAttempts + 1; i++) {
|
|
162
|
+
const withSuffix = `${candidate}${i}`;
|
|
163
|
+
if (withSuffix.length <= maxLength && (await isAvailable(withSuffix))) {
|
|
164
|
+
return withSuffix;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Final fallback: random username
|
|
169
|
+
const random = Math.random().toString(36).slice(2, 8);
|
|
170
|
+
return `user${random}`;
|
|
171
|
+
}
|
|
172
|
+
|
|
97
173
|
/**
|
|
98
174
|
* Random delay to prevent timing attacks (50-200ms)
|
|
99
175
|
* Uses cryptographically secure random number generation
|
|
@@ -102,6 +178,59 @@ export class SessionService {
|
|
|
102
178
|
return new Promise((resolve) => setTimeout(resolve, randomInt(50, 201)));
|
|
103
179
|
}
|
|
104
180
|
|
|
181
|
+
protected static readonly LOGIN_CACHE_NAME = "login-rate-limit";
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Check if a login key is currently locked out.
|
|
185
|
+
* Read-only — does not increment the counter.
|
|
186
|
+
*/
|
|
187
|
+
protected async isLoginLocked(key: string, max: number): Promise<boolean> {
|
|
188
|
+
try {
|
|
189
|
+
const count = await this.cacheProvider.getTyped<number>(
|
|
190
|
+
SessionService.LOGIN_CACHE_NAME,
|
|
191
|
+
key,
|
|
192
|
+
);
|
|
193
|
+
return count != null && count >= max;
|
|
194
|
+
} catch (error) {
|
|
195
|
+
this.log.warn(
|
|
196
|
+
"Failed to check login rate limit, allowing attempt",
|
|
197
|
+
error,
|
|
198
|
+
);
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Record a failed login attempt. Uses getTyped + setTyped (not incr) so that
|
|
205
|
+
* each write refreshes the TTL — implementing sliding-window behavior.
|
|
206
|
+
*
|
|
207
|
+
* Returns `true` if this failure just crossed the lockout threshold.
|
|
208
|
+
*/
|
|
209
|
+
protected async recordFailedLogin(
|
|
210
|
+
key: string,
|
|
211
|
+
max: number,
|
|
212
|
+
windowMs: number,
|
|
213
|
+
): Promise<boolean> {
|
|
214
|
+
try {
|
|
215
|
+
const count =
|
|
216
|
+
(await this.cacheProvider.getTyped<number>(
|
|
217
|
+
SessionService.LOGIN_CACHE_NAME,
|
|
218
|
+
key,
|
|
219
|
+
)) ?? 0;
|
|
220
|
+
const newCount = count + 1;
|
|
221
|
+
await this.cacheProvider.setTyped(
|
|
222
|
+
SessionService.LOGIN_CACHE_NAME,
|
|
223
|
+
key,
|
|
224
|
+
newCount,
|
|
225
|
+
{ ttl: windowMs },
|
|
226
|
+
);
|
|
227
|
+
return newCount === max;
|
|
228
|
+
} catch (error) {
|
|
229
|
+
this.log.warn("Failed to record failed login attempt", error);
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
105
234
|
/**
|
|
106
235
|
* Validate user credentials and return the user if valid.
|
|
107
236
|
*/
|
|
@@ -111,13 +240,33 @@ export class SessionService {
|
|
|
111
240
|
password: string,
|
|
112
241
|
userRealmName?: string,
|
|
113
242
|
): Promise<UserEntity> {
|
|
114
|
-
const
|
|
243
|
+
const realm = this.realmProvider.getRealm(userRealmName);
|
|
244
|
+
const settings = await realm.getSettings();
|
|
245
|
+
const { name } = realm;
|
|
246
|
+
const { loginRateLimit } = settings;
|
|
115
247
|
const isEmail = username.includes("@");
|
|
116
248
|
const isPhone = /^[+\d][\d\s()-]+$/.test(username);
|
|
117
249
|
const isUsername = !isEmail && !isPhone;
|
|
118
250
|
const identities = this.identities(userRealmName);
|
|
119
251
|
const users = this.users(userRealmName);
|
|
120
252
|
|
|
253
|
+
// IP rate limit check (global, cross-realm) — before any DB work
|
|
254
|
+
const request = this.alepha.store.get("alepha.http.request");
|
|
255
|
+
const ipKey = request?.ip ? `login:ip:${request.ip}` : undefined;
|
|
256
|
+
|
|
257
|
+
if (ipKey) {
|
|
258
|
+
const ipLocked = await this.isLoginLocked(
|
|
259
|
+
ipKey,
|
|
260
|
+
loginRateLimit.ipMaxAttempts,
|
|
261
|
+
);
|
|
262
|
+
if (ipLocked) {
|
|
263
|
+
this.log.warn("Login blocked — IP rate limit exceeded", {
|
|
264
|
+
ip: request?.ip,
|
|
265
|
+
});
|
|
266
|
+
throw new InvalidCredentialsError();
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
121
270
|
await this.randomDelay();
|
|
122
271
|
|
|
123
272
|
try {
|
|
@@ -145,7 +294,7 @@ export class SessionService {
|
|
|
145
294
|
throw new InvalidCredentialsError();
|
|
146
295
|
}
|
|
147
296
|
}
|
|
148
|
-
where.username = username;
|
|
297
|
+
where.username = { ilike: username };
|
|
149
298
|
} else if (settings.email !== "none" && isEmail) {
|
|
150
299
|
where.email = username;
|
|
151
300
|
} else if (settings.phoneNumber !== "none" && isPhone) {
|
|
@@ -180,6 +329,42 @@ export class SessionService {
|
|
|
180
329
|
metadata: { provider, username },
|
|
181
330
|
});
|
|
182
331
|
|
|
332
|
+
// Only increment IP counter (no user ID to track)
|
|
333
|
+
if (ipKey) {
|
|
334
|
+
const justLocked = await this.recordFailedLogin(
|
|
335
|
+
ipKey,
|
|
336
|
+
loginRateLimit.ipMaxAttempts,
|
|
337
|
+
loginRateLimit.windowMs,
|
|
338
|
+
);
|
|
339
|
+
if (justLocked) {
|
|
340
|
+
await this.userAudits(userRealmName)?.record(
|
|
341
|
+
"security",
|
|
342
|
+
"rate_limited",
|
|
343
|
+
{
|
|
344
|
+
userRealm: name,
|
|
345
|
+
success: false,
|
|
346
|
+
description:
|
|
347
|
+
"IP temporarily locked due to too many failed login attempts",
|
|
348
|
+
metadata: { ip: request?.ip },
|
|
349
|
+
},
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
throw new InvalidCredentialsError();
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Account rate limit check (per-realm)
|
|
358
|
+
const accountKey = `login:account:${name}:${user.id}`;
|
|
359
|
+
const accountLocked = await this.isLoginLocked(
|
|
360
|
+
accountKey,
|
|
361
|
+
loginRateLimit.accountMaxAttempts,
|
|
362
|
+
);
|
|
363
|
+
if (accountLocked) {
|
|
364
|
+
this.log.warn("Login blocked — account rate limit exceeded", {
|
|
365
|
+
userId: user.id,
|
|
366
|
+
realm: name,
|
|
367
|
+
});
|
|
183
368
|
throw new InvalidCredentialsError();
|
|
184
369
|
}
|
|
185
370
|
|
|
@@ -220,6 +405,48 @@ export class SessionService {
|
|
|
220
405
|
metadata: { provider, username },
|
|
221
406
|
});
|
|
222
407
|
|
|
408
|
+
// Record failed attempt on both IP and account counters
|
|
409
|
+
if (ipKey) {
|
|
410
|
+
const ipJustLocked = await this.recordFailedLogin(
|
|
411
|
+
ipKey,
|
|
412
|
+
loginRateLimit.ipMaxAttempts,
|
|
413
|
+
loginRateLimit.windowMs,
|
|
414
|
+
);
|
|
415
|
+
if (ipJustLocked) {
|
|
416
|
+
await this.userAudits(userRealmName)?.record(
|
|
417
|
+
"security",
|
|
418
|
+
"rate_limited",
|
|
419
|
+
{
|
|
420
|
+
userRealm: name,
|
|
421
|
+
success: false,
|
|
422
|
+
description:
|
|
423
|
+
"IP temporarily locked due to too many failed login attempts",
|
|
424
|
+
metadata: { ip: request?.ip },
|
|
425
|
+
},
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
const accountJustLocked = await this.recordFailedLogin(
|
|
431
|
+
accountKey,
|
|
432
|
+
loginRateLimit.accountMaxAttempts,
|
|
433
|
+
loginRateLimit.windowMs,
|
|
434
|
+
);
|
|
435
|
+
if (accountJustLocked) {
|
|
436
|
+
await this.userAudits(userRealmName)?.record(
|
|
437
|
+
"security",
|
|
438
|
+
"rate_limited",
|
|
439
|
+
{
|
|
440
|
+
userRealm: name,
|
|
441
|
+
resourceId: user.id,
|
|
442
|
+
success: false,
|
|
443
|
+
description:
|
|
444
|
+
"Account temporarily locked due to too many failed login attempts",
|
|
445
|
+
metadata: { userId: user.id },
|
|
446
|
+
},
|
|
447
|
+
);
|
|
448
|
+
}
|
|
449
|
+
|
|
223
450
|
throw new InvalidCredentialsError();
|
|
224
451
|
}
|
|
225
452
|
|
|
@@ -238,7 +465,6 @@ export class SessionService {
|
|
|
238
465
|
return user;
|
|
239
466
|
} catch (error) {
|
|
240
467
|
if (error instanceof InvalidCredentialsError) {
|
|
241
|
-
// TODO: store failed login attempts (with request data) and lock account after threshold
|
|
242
468
|
throw error;
|
|
243
469
|
}
|
|
244
470
|
|
|
@@ -442,12 +668,27 @@ export class SessionService {
|
|
|
442
668
|
return existing;
|
|
443
669
|
}
|
|
444
670
|
|
|
445
|
-
|
|
446
|
-
|
|
671
|
+
const realmSettings = await realm.getSettings();
|
|
672
|
+
const adminEmails = realmSettings?.adminEmails ?? [];
|
|
673
|
+
const isAdmin = profile.email && adminEmails.includes(profile.email);
|
|
674
|
+
|
|
675
|
+
if (realmSettings?.registrationAllowed === false && !isAdmin) {
|
|
676
|
+
this.log.warn("Registration not allowed for realm via OAuth2", {
|
|
677
|
+
provider,
|
|
678
|
+
userRealmName,
|
|
679
|
+
});
|
|
680
|
+
throw new BadRequestError("Account doesn't exist");
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
const username = await this.generateUniqueUsername(
|
|
684
|
+
profile,
|
|
685
|
+
realmSettings,
|
|
686
|
+
users,
|
|
687
|
+
);
|
|
447
688
|
|
|
448
689
|
const user = await users.create({
|
|
449
690
|
realm: realm.name,
|
|
450
|
-
username
|
|
691
|
+
username,
|
|
451
692
|
email: profile.email,
|
|
452
693
|
// we trust the OAuth2 provider
|
|
453
694
|
emailVerified: true,
|
|
@@ -276,7 +276,7 @@ export class UserService {
|
|
|
276
276
|
// Check for existing user based on provided unique fields
|
|
277
277
|
if (data.username) {
|
|
278
278
|
const existingUser = await this.users(userRealmName).findOne({
|
|
279
|
-
where: { username: {
|
|
279
|
+
where: { username: { ilike: data.username } },
|
|
280
280
|
});
|
|
281
281
|
|
|
282
282
|
if (existingUser) {
|