@de-otio/trellis 0.6.1 → 0.7.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/dist/env.d.ts +21 -0
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +12 -0
- package/dist/env.js.map +1 -1
- package/dist/lambda/nightly-cron.d.ts.map +1 -1
- package/dist/lambda/nightly-cron.js +5 -2
- package/dist/lambda/nightly-cron.js.map +1 -1
- package/dist/lambda/post-confirmation.d.ts +30 -0
- package/dist/lambda/post-confirmation.d.ts.map +1 -1
- package/dist/lambda/post-confirmation.js +333 -29
- package/dist/lambda/post-confirmation.js.map +1 -1
- package/dist/lambda/pre-token-generation.d.ts +20 -0
- package/dist/lambda/pre-token-generation.d.ts.map +1 -1
- package/dist/lambda/pre-token-generation.js +233 -48
- package/dist/lambda/pre-token-generation.js.map +1 -1
- package/dist/lib/activitypub/activity-processor.d.ts.map +1 -1
- package/dist/lib/activitypub/activity-processor.js +2 -1
- package/dist/lib/activitypub/activity-processor.js.map +1 -1
- package/dist/lib/activitypub/group-service.d.ts +2 -2
- package/dist/lib/activitypub/group-service.d.ts.map +1 -1
- package/dist/lib/activitypub/group-service.js +5 -2
- package/dist/lib/activitypub/group-service.js.map +1 -1
- package/dist/lib/age-tier-transition.d.ts.map +1 -1
- package/dist/lib/age-tier-transition.js +19 -10
- package/dist/lib/age-tier-transition.js.map +1 -1
- package/dist/lib/audit/csv-export.d.ts +25 -0
- package/dist/lib/audit/csv-export.d.ts.map +1 -0
- package/dist/lib/audit/csv-export.js +54 -0
- package/dist/lib/audit/csv-export.js.map +1 -0
- package/dist/lib/audit/emit.d.ts +56 -0
- package/dist/lib/audit/emit.d.ts.map +1 -0
- package/dist/lib/audit/emit.js +124 -0
- package/dist/lib/audit/emit.js.map +1 -0
- package/dist/lib/audit/event-types.d.ts +36 -0
- package/dist/lib/audit/event-types.d.ts.map +1 -0
- package/dist/lib/audit/event-types.js +69 -0
- package/dist/lib/audit/event-types.js.map +1 -0
- package/dist/lib/audit/pii-filter.d.ts +22 -0
- package/dist/lib/audit/pii-filter.d.ts.map +1 -0
- package/dist/lib/audit/pii-filter.js +51 -0
- package/dist/lib/audit/pii-filter.js.map +1 -0
- package/dist/lib/audit-logger.js +1 -1
- package/dist/lib/audit-logger.js.map +1 -1
- package/dist/lib/auth/auth-context.d.ts +34 -0
- package/dist/lib/auth/auth-context.d.ts.map +1 -0
- package/dist/lib/auth/auth-context.js +10 -0
- package/dist/lib/auth/auth-context.js.map +1 -0
- package/dist/lib/auth/auth-middleware.d.ts +50 -0
- package/dist/lib/auth/auth-middleware.d.ts.map +1 -0
- package/dist/lib/auth/auth-middleware.js +153 -0
- package/dist/lib/auth/auth-middleware.js.map +1 -0
- package/dist/lib/auth/capabilities.d.ts +40 -0
- package/dist/lib/auth/capabilities.d.ts.map +1 -0
- package/dist/lib/auth/capabilities.js +44 -0
- package/dist/lib/auth/capabilities.js.map +1 -0
- package/dist/lib/auth/claims-cache.d.ts +70 -0
- package/dist/lib/auth/claims-cache.d.ts.map +1 -0
- package/dist/lib/auth/claims-cache.js +139 -0
- package/dist/lib/auth/claims-cache.js.map +1 -0
- package/dist/lib/auth/cognito-jwt.d.ts +6 -0
- package/dist/lib/auth/cognito-jwt.d.ts.map +1 -1
- package/dist/lib/auth/cognito-jwt.js.map +1 -1
- package/dist/lib/auth/idp-redirect-builder.d.ts +43 -0
- package/dist/lib/auth/idp-redirect-builder.d.ts.map +1 -0
- package/dist/lib/auth/idp-redirect-builder.js +48 -0
- package/dist/lib/auth/idp-redirect-builder.js.map +1 -0
- package/dist/lib/auth/require.d.ts +51 -0
- package/dist/lib/auth/require.d.ts.map +1 -0
- package/dist/lib/auth/require.js +99 -0
- package/dist/lib/auth/require.js.map +1 -0
- package/dist/lib/auth/role-grants.d.ts +18 -0
- package/dist/lib/auth/role-grants.d.ts.map +1 -0
- package/dist/lib/auth/role-grants.js +62 -0
- package/dist/lib/auth/role-grants.js.map +1 -0
- package/dist/lib/cognito/idp-sdk.d.ts +80 -0
- package/dist/lib/cognito/idp-sdk.d.ts.map +1 -0
- package/dist/lib/cognito/idp-sdk.js +186 -0
- package/dist/lib/cognito/idp-sdk.js.map +1 -0
- package/dist/lib/cognito/issuer-probe.d.ts +47 -0
- package/dist/lib/cognito/issuer-probe.d.ts.map +1 -0
- package/dist/lib/cognito/issuer-probe.js +319 -0
- package/dist/lib/cognito/issuer-probe.js.map +1 -0
- package/dist/lib/comment-handler.d.ts +7 -7
- package/dist/lib/comment-handler.d.ts.map +1 -1
- package/dist/lib/comment-handler.js +23 -20
- package/dist/lib/comment-handler.js.map +1 -1
- package/dist/lib/compliance/baseline.d.ts +15 -0
- package/dist/lib/compliance/baseline.d.ts.map +1 -0
- package/dist/lib/compliance/baseline.js +205 -0
- package/dist/lib/compliance/baseline.js.map +1 -0
- package/dist/lib/compliance/tenant-merge.d.ts +35 -0
- package/dist/lib/compliance/tenant-merge.d.ts.map +1 -0
- package/dist/lib/compliance/tenant-merge.js +80 -0
- package/dist/lib/compliance/tenant-merge.js.map +1 -0
- package/dist/lib/compliance/types.d.ts +135 -0
- package/dist/lib/compliance/types.d.ts.map +1 -0
- package/dist/lib/compliance/types.js +9 -0
- package/dist/lib/compliance/types.js.map +1 -0
- package/dist/lib/connection-code-handler.d.ts +4 -4
- package/dist/lib/connection-code-handler.d.ts.map +1 -1
- package/dist/lib/connection-code-handler.js +21 -11
- package/dist/lib/connection-code-handler.js.map +1 -1
- package/dist/lib/feed-handler.d.ts +2 -2
- package/dist/lib/feed-handler.d.ts.map +1 -1
- package/dist/lib/feed-handler.js +5 -9
- package/dist/lib/feed-handler.js.map +1 -1
- package/dist/lib/middleware/idempotency-store.d.ts +86 -0
- package/dist/lib/middleware/idempotency-store.d.ts.map +1 -0
- package/dist/lib/middleware/idempotency-store.js +109 -0
- package/dist/lib/middleware/idempotency-store.js.map +1 -0
- package/dist/lib/middleware/idempotency.d.ts +37 -0
- package/dist/lib/middleware/idempotency.d.ts.map +1 -0
- package/dist/lib/middleware/idempotency.js +358 -0
- package/dist/lib/middleware/idempotency.js.map +1 -0
- package/dist/lib/net/trusted-client-ip.d.ts +39 -0
- package/dist/lib/net/trusted-client-ip.d.ts.map +1 -0
- package/dist/lib/net/trusted-client-ip.js +100 -0
- package/dist/lib/net/trusted-client-ip.js.map +1 -0
- package/dist/lib/notification-handler.d.ts +5 -5
- package/dist/lib/notification-handler.d.ts.map +1 -1
- package/dist/lib/notification-handler.js +11 -9
- package/dist/lib/notification-handler.js.map +1 -1
- package/dist/lib/oauth/cognito-issuer.d.ts +34 -0
- package/dist/lib/oauth/cognito-issuer.d.ts.map +1 -0
- package/dist/lib/oauth/cognito-issuer.js +53 -0
- package/dist/lib/oauth/cognito-issuer.js.map +1 -0
- package/dist/lib/oauth/device-authorization.d.ts +145 -0
- package/dist/lib/oauth/device-authorization.d.ts.map +1 -0
- package/dist/lib/oauth/device-authorization.js +312 -0
- package/dist/lib/oauth/device-authorization.js.map +1 -0
- package/dist/lib/oauth/envelope-crypto.d.ts +101 -0
- package/dist/lib/oauth/envelope-crypto.d.ts.map +1 -0
- package/dist/lib/oauth/envelope-crypto.js +223 -0
- package/dist/lib/oauth/envelope-crypto.js.map +1 -0
- package/dist/lib/oauth/refresh-detection.d.ts +126 -0
- package/dist/lib/oauth/refresh-detection.d.ts.map +1 -0
- package/dist/lib/oauth/refresh-detection.js +248 -0
- package/dist/lib/oauth/refresh-detection.js.map +1 -0
- package/dist/lib/openapi/generator.d.ts +78 -0
- package/dist/lib/openapi/generator.d.ts.map +1 -0
- package/dist/lib/openapi/generator.js +201 -0
- package/dist/lib/openapi/generator.js.map +1 -0
- package/dist/lib/post-handler.d.ts +1 -1
- package/dist/lib/post-handler.d.ts.map +1 -1
- package/dist/lib/post-handler.js +4 -15
- package/dist/lib/post-handler.js.map +1 -1
- package/dist/lib/rate-limit.d.ts.map +1 -1
- package/dist/lib/rate-limit.js +11 -3
- package/dist/lib/rate-limit.js.map +1 -1
- package/dist/lib/routes/agent-authorize.d.ts +32 -0
- package/dist/lib/routes/agent-authorize.d.ts.map +1 -0
- package/dist/lib/routes/agent-authorize.js +479 -0
- package/dist/lib/routes/agent-authorize.js.map +1 -0
- package/dist/lib/routes/agent-sessions.d.ts +20 -0
- package/dist/lib/routes/agent-sessions.d.ts.map +1 -0
- package/dist/lib/routes/agent-sessions.js +124 -0
- package/dist/lib/routes/agent-sessions.js.map +1 -0
- package/dist/lib/routes/agent-surface.d.ts +37 -0
- package/dist/lib/routes/agent-surface.d.ts.map +1 -0
- package/dist/lib/routes/agent-surface.js +208 -0
- package/dist/lib/routes/agent-surface.js.map +1 -0
- package/dist/lib/routes/auth-discover.d.ts +18 -0
- package/dist/lib/routes/auth-discover.d.ts.map +1 -0
- package/dist/lib/routes/auth-discover.js +177 -0
- package/dist/lib/routes/auth-discover.js.map +1 -0
- package/dist/lib/routes/comments.d.ts.map +1 -1
- package/dist/lib/routes/comments.js +36 -7
- package/dist/lib/routes/comments.js.map +1 -1
- package/dist/lib/routes/connection-codes.d.ts.map +1 -1
- package/dist/lib/routes/connection-codes.js +21 -4
- package/dist/lib/routes/connection-codes.js.map +1 -1
- package/dist/lib/routes/content-discovery.d.ts.map +1 -1
- package/dist/lib/routes/content-discovery.js +18 -13
- package/dist/lib/routes/content-discovery.js.map +1 -1
- package/dist/lib/routes/dashboard.js +1 -1
- package/dist/lib/routes/dashboard.js.map +1 -1
- package/dist/lib/routes/employees.d.ts.map +1 -1
- package/dist/lib/routes/employees.js +57 -15
- package/dist/lib/routes/employees.js.map +1 -1
- package/dist/lib/routes/entities.d.ts.map +1 -1
- package/dist/lib/routes/entities.js +35 -19
- package/dist/lib/routes/entities.js.map +1 -1
- package/dist/lib/routes/errors.d.ts +34 -0
- package/dist/lib/routes/errors.d.ts.map +1 -0
- package/dist/lib/routes/errors.js +57 -0
- package/dist/lib/routes/errors.js.map +1 -0
- package/dist/lib/routes/feeds.d.ts.map +1 -1
- package/dist/lib/routes/feeds.js +12 -2
- package/dist/lib/routes/feeds.js.map +1 -1
- package/dist/lib/routes/index.d.ts.map +1 -1
- package/dist/lib/routes/index.js +50 -0
- package/dist/lib/routes/index.js.map +1 -1
- package/dist/lib/routes/mfa.d.ts.map +1 -1
- package/dist/lib/routes/mfa.js +1 -0
- package/dist/lib/routes/mfa.js.map +1 -1
- package/dist/lib/routes/notifications.d.ts.map +1 -1
- package/dist/lib/routes/notifications.js +21 -4
- package/dist/lib/routes/notifications.js.map +1 -1
- package/dist/lib/routes/oauth.d.ts +15 -0
- package/dist/lib/routes/oauth.d.ts.map +1 -0
- package/dist/lib/routes/oauth.js +139 -0
- package/dist/lib/routes/oauth.js.map +1 -0
- package/dist/lib/routes/posts.d.ts.map +1 -1
- package/dist/lib/routes/posts.js +30 -19
- package/dist/lib/routes/posts.js.map +1 -1
- package/dist/lib/routes/products.d.ts.map +1 -1
- package/dist/lib/routes/products.js +19 -22
- package/dist/lib/routes/products.js.map +1 -1
- package/dist/lib/routes/setup-status.d.ts +34 -0
- package/dist/lib/routes/setup-status.d.ts.map +1 -0
- package/dist/lib/routes/setup-status.js +87 -0
- package/dist/lib/routes/setup-status.js.map +1 -0
- package/dist/lib/routes/taxonomy-analytics.d.ts.map +1 -1
- package/dist/lib/routes/taxonomy-analytics.js +15 -14
- package/dist/lib/routes/taxonomy-analytics.js.map +1 -1
- package/dist/lib/routes/taxonomy.d.ts.map +1 -1
- package/dist/lib/routes/taxonomy.js +19 -16
- package/dist/lib/routes/taxonomy.js.map +1 -1
- package/dist/lib/routes/tenant-audit.d.ts +19 -0
- package/dist/lib/routes/tenant-audit.d.ts.map +1 -0
- package/dist/lib/routes/tenant-audit.js +244 -0
- package/dist/lib/routes/tenant-audit.js.map +1 -0
- package/dist/lib/routes/tenant-compliance.d.ts +21 -0
- package/dist/lib/routes/tenant-compliance.d.ts.map +1 -0
- package/dist/lib/routes/tenant-compliance.js +122 -0
- package/dist/lib/routes/tenant-compliance.js.map +1 -0
- package/dist/lib/routes/tenant-domains.d.ts +11 -0
- package/dist/lib/routes/tenant-domains.d.ts.map +1 -0
- package/dist/lib/routes/tenant-domains.js +95 -0
- package/dist/lib/routes/tenant-domains.js.map +1 -0
- package/dist/lib/routes/tenant-idp.d.ts +3 -0
- package/dist/lib/routes/tenant-idp.d.ts.map +1 -0
- package/dist/lib/routes/tenant-idp.js +89 -0
- package/dist/lib/routes/tenant-idp.js.map +1 -0
- package/dist/lib/routes/tenant-members.d.ts +13 -0
- package/dist/lib/routes/tenant-members.d.ts.map +1 -0
- package/dist/lib/routes/tenant-members.js +75 -0
- package/dist/lib/routes/tenant-members.js.map +1 -0
- package/dist/lib/routes/tenant-role-mappings.d.ts +11 -0
- package/dist/lib/routes/tenant-role-mappings.d.ts.map +1 -0
- package/dist/lib/routes/tenant-role-mappings.js +90 -0
- package/dist/lib/routes/tenant-role-mappings.js.map +1 -0
- package/dist/lib/routes/tenants.d.ts +13 -0
- package/dist/lib/routes/tenants.d.ts.map +1 -0
- package/dist/lib/routes/tenants.js +121 -0
- package/dist/lib/routes/tenants.js.map +1 -0
- package/dist/lib/routes/types.d.ts +9 -0
- package/dist/lib/routes/types.d.ts.map +1 -1
- package/dist/lib/schemas.d.ts +2 -2
- package/dist/lib/secrets/idp-secrets.d.ts +51 -0
- package/dist/lib/secrets/idp-secrets.d.ts.map +1 -0
- package/dist/lib/secrets/idp-secrets.js +111 -0
- package/dist/lib/secrets/idp-secrets.js.map +1 -0
- package/dist/lib/security-monitor.d.ts.map +1 -1
- package/dist/lib/security-monitor.js +6 -1
- package/dist/lib/security-monitor.js.map +1 -1
- package/dist/lib/session-manager.d.ts +1 -0
- package/dist/lib/session-manager.d.ts.map +1 -1
- package/dist/lib/session-manager.js.map +1 -1
- package/dist/lib/taxonomy-handler-factory.d.ts +4 -2
- package/dist/lib/taxonomy-handler-factory.d.ts.map +1 -1
- package/dist/lib/taxonomy-handler-factory.js +8 -7
- package/dist/lib/taxonomy-handler-factory.js.map +1 -1
- package/dist/lib/tenant/audit-emit.d.ts +18 -0
- package/dist/lib/tenant/audit-emit.d.ts.map +1 -0
- package/dist/lib/tenant/audit-emit.js +16 -0
- package/dist/lib/tenant/audit-emit.js.map +1 -0
- package/dist/lib/tenant/derive-domain.d.ts +19 -0
- package/dist/lib/tenant/derive-domain.d.ts.map +1 -0
- package/dist/lib/tenant/derive-domain.js +38 -0
- package/dist/lib/tenant/derive-domain.js.map +1 -0
- package/dist/lib/tenant/domain-handler.d.ts +42 -0
- package/dist/lib/tenant/domain-handler.d.ts.map +1 -0
- package/dist/lib/tenant/domain-handler.js +344 -0
- package/dist/lib/tenant/domain-handler.js.map +1 -0
- package/dist/lib/tenant/domain-validator.d.ts +28 -0
- package/dist/lib/tenant/domain-validator.d.ts.map +1 -0
- package/dist/lib/tenant/domain-validator.js +145 -0
- package/dist/lib/tenant/domain-validator.js.map +1 -0
- package/dist/lib/tenant/domain-verifier.d.ts +30 -0
- package/dist/lib/tenant/domain-verifier.d.ts.map +1 -0
- package/dist/lib/tenant/domain-verifier.js +53 -0
- package/dist/lib/tenant/domain-verifier.js.map +1 -0
- package/dist/lib/tenant/idp-handler.d.ts +29 -0
- package/dist/lib/tenant/idp-handler.d.ts.map +1 -0
- package/dist/lib/tenant/idp-handler.js +693 -0
- package/dist/lib/tenant/idp-handler.js.map +1 -0
- package/dist/lib/tenant/idp-name.d.ts +2 -0
- package/dist/lib/tenant/idp-name.d.ts.map +1 -0
- package/dist/lib/tenant/idp-name.js +20 -0
- package/dist/lib/tenant/idp-name.js.map +1 -0
- package/dist/lib/tenant/member-handler.d.ts +31 -0
- package/dist/lib/tenant/member-handler.d.ts.map +1 -0
- package/dist/lib/tenant/member-handler.js +343 -0
- package/dist/lib/tenant/member-handler.js.map +1 -0
- package/dist/lib/tenant/reserved-slugs.d.ts +37 -0
- package/dist/lib/tenant/reserved-slugs.d.ts.map +1 -0
- package/dist/lib/tenant/reserved-slugs.js +116 -0
- package/dist/lib/tenant/reserved-slugs.js.map +1 -0
- package/dist/lib/tenant/resolve-role.d.ts +39 -0
- package/dist/lib/tenant/resolve-role.d.ts.map +1 -0
- package/dist/lib/tenant/resolve-role.js +60 -0
- package/dist/lib/tenant/resolve-role.js.map +1 -0
- package/dist/lib/tenant/role-mapping-handler.d.ts +26 -0
- package/dist/lib/tenant/role-mapping-handler.d.ts.map +1 -0
- package/dist/lib/tenant/role-mapping-handler.js +260 -0
- package/dist/lib/tenant/role-mapping-handler.js.map +1 -0
- package/dist/lib/tenant/setup-status.d.ts +83 -0
- package/dist/lib/tenant/setup-status.d.ts.map +1 -0
- package/dist/lib/tenant/setup-status.js +201 -0
- package/dist/lib/tenant/setup-status.js.map +1 -0
- package/dist/lib/tenant/slug-validator.d.ts +31 -0
- package/dist/lib/tenant/slug-validator.d.ts.map +1 -0
- package/dist/lib/tenant/slug-validator.js +42 -0
- package/dist/lib/tenant/slug-validator.js.map +1 -0
- package/dist/lib/tenant/tenant-handler.d.ts +49 -0
- package/dist/lib/tenant/tenant-handler.d.ts.map +1 -0
- package/dist/lib/tenant/tenant-handler.js +377 -0
- package/dist/lib/tenant/tenant-handler.js.map +1 -0
- package/dist/lib/tenant/transfer-ownership.d.ts +39 -0
- package/dist/lib/tenant/transfer-ownership.d.ts.map +1 -0
- package/dist/lib/tenant/transfer-ownership.js +66 -0
- package/dist/lib/tenant/transfer-ownership.js.map +1 -0
- package/dist/lib/user/derive-handle.d.ts +29 -0
- package/dist/lib/user/derive-handle.d.ts.map +1 -0
- package/dist/lib/user/derive-handle.js +65 -0
- package/dist/lib/user/derive-handle.js.map +1 -0
- package/dist/lib/user-deprovisioning.d.ts +11 -1
- package/dist/lib/user-deprovisioning.d.ts.map +1 -1
- package/dist/lib/user-deprovisioning.js +46 -2
- package/dist/lib/user-deprovisioning.js.map +1 -1
- package/dist/lib/validation/feature-toggle-schemas.d.ts +10 -10
- package/package.json +5 -3
- package/prisma/migrations/20260502094501_add_tenancy_model/migration.sql +334 -0
- package/prisma/migrations/20260503000000_add_tenant_region/migration.sql +4 -0
- package/prisma/schema.prisma +324 -74
- package/src/lambda/nightly-cron.ts +4 -1
- package/src/lambda/post-confirmation.ts +405 -29
- package/src/lambda/pre-token-generation.ts +300 -59
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Tenant Domain Handler
|
|
4
|
+
*
|
|
5
|
+
* Handles CRUD + DNS verification for tenant-claimed domains.
|
|
6
|
+
*
|
|
7
|
+
* Routes:
|
|
8
|
+
* POST /api/tenants/:id/domains — claim domain
|
|
9
|
+
* GET /api/tenants/:id/domains — list domains
|
|
10
|
+
* DELETE /api/tenants/:id/domains/:domainId — remove domain
|
|
11
|
+
* POST /api/tenants/:id/domains/:domainId/verify — trigger DNS check
|
|
12
|
+
*/
|
|
13
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
16
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
17
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
18
|
+
}
|
|
19
|
+
Object.defineProperty(o, k2, desc);
|
|
20
|
+
}) : (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
o[k2] = m[k];
|
|
23
|
+
}));
|
|
24
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
25
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
26
|
+
}) : function(o, v) {
|
|
27
|
+
o["default"] = v;
|
|
28
|
+
});
|
|
29
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
30
|
+
var ownKeys = function(o) {
|
|
31
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
32
|
+
var ar = [];
|
|
33
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
34
|
+
return ar;
|
|
35
|
+
};
|
|
36
|
+
return ownKeys(o);
|
|
37
|
+
};
|
|
38
|
+
return function (mod) {
|
|
39
|
+
if (mod && mod.__esModule) return mod;
|
|
40
|
+
var result = {};
|
|
41
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
42
|
+
__setModuleDefault(result, mod);
|
|
43
|
+
return result;
|
|
44
|
+
};
|
|
45
|
+
})();
|
|
46
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
+
exports.DomainHandler = void 0;
|
|
48
|
+
const node_crypto_1 = require("node:crypto");
|
|
49
|
+
const auth_middleware_1 = require("../auth/auth-middleware");
|
|
50
|
+
const require_1 = require("../auth/require");
|
|
51
|
+
const domain_validator_1 = require("./domain-validator");
|
|
52
|
+
const domain_verifier_1 = require("./domain-verifier");
|
|
53
|
+
// ─── Constants ───────────────────────────────────────────────────────────────
|
|
54
|
+
const TOKEN_EXPIRY_DAYS = 7;
|
|
55
|
+
const RATE_LIMIT_WINDOW_SECONDS = 3600; // 1 hour
|
|
56
|
+
const RATE_LIMIT_MAX_ATTEMPTS = 10;
|
|
57
|
+
const FAILURE_ROTATION_WINDOW_SECONDS = 86400; // 24 hours
|
|
58
|
+
const FAILURE_ROTATION_THRESHOLD = 10;
|
|
59
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
60
|
+
function generateToken() {
|
|
61
|
+
return (0, node_crypto_1.randomBytes)(16).toString("hex");
|
|
62
|
+
}
|
|
63
|
+
function tokenExpiresAt() {
|
|
64
|
+
const d = new Date();
|
|
65
|
+
d.setDate(d.getDate() + TOKEN_EXPIRY_DAYS);
|
|
66
|
+
return d;
|
|
67
|
+
}
|
|
68
|
+
function makeRateLimitKey(tenantId, domainId) {
|
|
69
|
+
return `domain-rate:${tenantId}:${domainId}`;
|
|
70
|
+
}
|
|
71
|
+
function makeFailureCountKey(tenantId, domainId) {
|
|
72
|
+
return `domain-fail:${tenantId}:${domainId}`;
|
|
73
|
+
}
|
|
74
|
+
function json(body, status) {
|
|
75
|
+
return new Response(JSON.stringify(body), {
|
|
76
|
+
status,
|
|
77
|
+
headers: { "content-type": "application/json" },
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
// ─── Handler ─────────────────────────────────────────────────────────────────
|
|
81
|
+
class DomainHandler {
|
|
82
|
+
/**
|
|
83
|
+
* POST /api/tenants/:id/domains
|
|
84
|
+
* Claim a domain. Returns 201 + token on first claim, 200 + existing record
|
|
85
|
+
* if the same tenant re-claims the same domain (idempotent).
|
|
86
|
+
* Returns 409 if the domain is already claimed by another tenant.
|
|
87
|
+
*/
|
|
88
|
+
async handleClaim(tenantId, request, auth, env) {
|
|
89
|
+
const denied = (0, auth_middleware_1.requireActiveTenant)(auth, tenantId) ??
|
|
90
|
+
(0, require_1.requireRole)(auth, "ADMIN");
|
|
91
|
+
if (denied)
|
|
92
|
+
return denied;
|
|
93
|
+
const { z } = await Promise.resolve().then(() => __importStar(require("zod")));
|
|
94
|
+
let body;
|
|
95
|
+
try {
|
|
96
|
+
body = await request.json();
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return json({ error: "INVALID_JSON", message: "Request body must be valid JSON" }, 400);
|
|
100
|
+
}
|
|
101
|
+
const schema = z.object({ domain: z.string() });
|
|
102
|
+
const parsed = schema.safeParse(body);
|
|
103
|
+
if (!parsed.success) {
|
|
104
|
+
return json({ error: "VALIDATION_ERROR", message: "domain is required" }, 400);
|
|
105
|
+
}
|
|
106
|
+
const validation = (0, domain_validator_1.validateDomain)(parsed.data.domain);
|
|
107
|
+
if (!validation.ok) {
|
|
108
|
+
return json({ error: validation.code, message: validation.message }, 400);
|
|
109
|
+
}
|
|
110
|
+
const { domain } = validation;
|
|
111
|
+
const { createPrisma } = await Promise.resolve().then(() => __importStar(require("../../db")));
|
|
112
|
+
const db = createPrisma(env);
|
|
113
|
+
// Check for existing record globally (without tenantId filter) to enforce
|
|
114
|
+
// the "domain can only belong to one tenant" rule.
|
|
115
|
+
const existing = await db.tenantDomain.findUnique({
|
|
116
|
+
where: { domain },
|
|
117
|
+
});
|
|
118
|
+
if (existing) {
|
|
119
|
+
if (existing.tenantId === tenantId) {
|
|
120
|
+
// Idempotent re-claim: check if token has expired and re-issue if so.
|
|
121
|
+
if (!existing.verifiedAt && existing.tokenExpiresAt < new Date()) {
|
|
122
|
+
// Expired unverified token — rotate it.
|
|
123
|
+
const updated = await db.tenantDomain.update({
|
|
124
|
+
where: { id: existing.id },
|
|
125
|
+
data: {
|
|
126
|
+
verificationToken: generateToken(),
|
|
127
|
+
tokenExpiresAt: tokenExpiresAt(),
|
|
128
|
+
verifyAttempts: 0,
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
return json(formatDomainRecord(updated), 200);
|
|
132
|
+
}
|
|
133
|
+
// Return existing record as-is.
|
|
134
|
+
return json(formatDomainRecord(existing), 200);
|
|
135
|
+
}
|
|
136
|
+
// Another tenant owns it — don't leak which tenant.
|
|
137
|
+
return json({ error: "DOMAIN_CONFLICT", message: "Domain is already claimed" }, 409);
|
|
138
|
+
}
|
|
139
|
+
// Create new record.
|
|
140
|
+
const record = await db.tenantDomain.create({
|
|
141
|
+
data: {
|
|
142
|
+
tenantId,
|
|
143
|
+
domain,
|
|
144
|
+
verificationToken: generateToken(),
|
|
145
|
+
tokenExpiresAt: tokenExpiresAt(),
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
return json(formatDomainRecord(record), 201);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* GET /api/tenants/:id/domains
|
|
152
|
+
* Lists tenant's domains. Tokens are included for unverified records
|
|
153
|
+
* (so the admin can copy the required TXT value).
|
|
154
|
+
*/
|
|
155
|
+
async handleList(tenantId, auth, env) {
|
|
156
|
+
const denied = (0, auth_middleware_1.requireActiveTenant)(auth, tenantId);
|
|
157
|
+
if (denied)
|
|
158
|
+
return denied;
|
|
159
|
+
const { createPrisma } = await Promise.resolve().then(() => __importStar(require("../../db")));
|
|
160
|
+
const db = createPrisma(env);
|
|
161
|
+
const domains = await db.tenantDomain.findMany({
|
|
162
|
+
where: { tenantId },
|
|
163
|
+
orderBy: { createdAt: "asc" },
|
|
164
|
+
});
|
|
165
|
+
return json({ domains: domains.map(formatDomainRecord) }, 200);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* DELETE /api/tenants/:id/domains/:domainId
|
|
169
|
+
* Removes a domain. Blocked if the domain is the only verified domain and
|
|
170
|
+
* the tenant has an ACTIVE IdP (removing it would break SSO logins).
|
|
171
|
+
*/
|
|
172
|
+
async handleDelete(tenantId, domainId, auth, env) {
|
|
173
|
+
const denied = (0, auth_middleware_1.requireActiveTenant)(auth, tenantId) ??
|
|
174
|
+
(0, require_1.requireRole)(auth, "ADMIN");
|
|
175
|
+
if (denied)
|
|
176
|
+
return denied;
|
|
177
|
+
const { createPrisma } = await Promise.resolve().then(() => __importStar(require("../../db")));
|
|
178
|
+
const db = createPrisma(env);
|
|
179
|
+
const record = await db.tenantDomain.findUnique({
|
|
180
|
+
where: { id: domainId, tenantId },
|
|
181
|
+
});
|
|
182
|
+
if (!record) {
|
|
183
|
+
return json({ error: "NOT_FOUND", message: "Domain not found" }, 404);
|
|
184
|
+
}
|
|
185
|
+
if (record.verifiedAt) {
|
|
186
|
+
// Check whether this is the last verified domain and there's an active IdP.
|
|
187
|
+
const verifiedCount = await db.tenantDomain.count({
|
|
188
|
+
where: { tenantId, verifiedAt: { not: null } },
|
|
189
|
+
});
|
|
190
|
+
if (verifiedCount === 1) {
|
|
191
|
+
const idp = await db.tenantIdentityProvider.findUnique({
|
|
192
|
+
where: { tenantId },
|
|
193
|
+
select: { status: true },
|
|
194
|
+
});
|
|
195
|
+
if (idp?.status === "ACTIVE") {
|
|
196
|
+
return json({
|
|
197
|
+
error: "DOMAIN_IN_USE",
|
|
198
|
+
message: "Cannot remove the only verified domain while an IdP is active",
|
|
199
|
+
remediation: "Disable the identity provider first, or add another verified domain",
|
|
200
|
+
}, 409);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
await db.tenantDomain.delete({ where: { id: domainId } });
|
|
205
|
+
return json({ ok: true }, 200);
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* POST /api/tenants/:id/domains/:domainId/verify
|
|
209
|
+
* Performs a DNS TXT lookup to verify domain ownership.
|
|
210
|
+
*/
|
|
211
|
+
async handleVerify(tenantId, domainId, auth, env) {
|
|
212
|
+
const denied = (0, auth_middleware_1.requireActiveTenant)(auth, tenantId) ??
|
|
213
|
+
(0, require_1.requireRole)(auth, "ADMIN");
|
|
214
|
+
if (denied)
|
|
215
|
+
return denied;
|
|
216
|
+
const { createPrisma } = await Promise.resolve().then(() => __importStar(require("../../db")));
|
|
217
|
+
const db = createPrisma(env);
|
|
218
|
+
const record = await db.tenantDomain.findUnique({
|
|
219
|
+
where: { id: domainId, tenantId },
|
|
220
|
+
});
|
|
221
|
+
if (!record) {
|
|
222
|
+
return json({ error: "NOT_FOUND", message: "Domain not found" }, 404);
|
|
223
|
+
}
|
|
224
|
+
// Already verified — return success immediately.
|
|
225
|
+
if (record.verifiedAt) {
|
|
226
|
+
return json({ ok: true, domain: record.domain, verifiedAt: record.verifiedAt }, 200);
|
|
227
|
+
}
|
|
228
|
+
// Check token expiry (sec finding #5).
|
|
229
|
+
if (record.tokenExpiresAt < new Date()) {
|
|
230
|
+
return json({
|
|
231
|
+
error: "TOKEN_EXPIRED",
|
|
232
|
+
message: "Verification token has expired",
|
|
233
|
+
remediation: "Re-claim the domain to get a new token",
|
|
234
|
+
}, 422);
|
|
235
|
+
}
|
|
236
|
+
// Rate limit: 10 verify attempts per hour per (tenantId, domainId).
|
|
237
|
+
const rateLimitKey = makeRateLimitKey(tenantId, domainId);
|
|
238
|
+
const rateLimitCheckResult = await this.checkAndIncrementRateLimit(env, rateLimitKey, RATE_LIMIT_MAX_ATTEMPTS, RATE_LIMIT_WINDOW_SECONDS);
|
|
239
|
+
if (rateLimitCheckResult.limited) {
|
|
240
|
+
return new Response(JSON.stringify({ error: "RATE_LIMITED", message: "Too many verify attempts" }), {
|
|
241
|
+
status: 429,
|
|
242
|
+
headers: {
|
|
243
|
+
"content-type": "application/json",
|
|
244
|
+
"Retry-After": String(rateLimitCheckResult.retryAfter),
|
|
245
|
+
},
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
// Perform DNS lookup.
|
|
249
|
+
const dnsResult = await (0, domain_verifier_1.verifyDomainToken)(record.domain, record.verificationToken);
|
|
250
|
+
if (dnsResult.verified) {
|
|
251
|
+
const updated = await db.tenantDomain.update({
|
|
252
|
+
where: { id: domainId },
|
|
253
|
+
data: {
|
|
254
|
+
verifiedAt: new Date(),
|
|
255
|
+
verifyAttemptedAt: new Date(),
|
|
256
|
+
verifyAttempts: { increment: 1 },
|
|
257
|
+
},
|
|
258
|
+
});
|
|
259
|
+
return json({ ok: true, domain: updated.domain, verifiedAt: updated.verifiedAt }, 200);
|
|
260
|
+
}
|
|
261
|
+
// Failed — increment failure counter in DynamoDB.
|
|
262
|
+
await db.tenantDomain.update({
|
|
263
|
+
where: { id: domainId },
|
|
264
|
+
data: {
|
|
265
|
+
verifyAttemptedAt: new Date(),
|
|
266
|
+
verifyAttempts: { increment: 1 },
|
|
267
|
+
},
|
|
268
|
+
});
|
|
269
|
+
// Check for auto-rotation after 10 failures in 24h.
|
|
270
|
+
const failKey = makeFailureCountKey(tenantId, domainId);
|
|
271
|
+
const newFailCount = await this.incrementFailureCount(env, failKey, FAILURE_ROTATION_WINDOW_SECONDS);
|
|
272
|
+
let rotated = false;
|
|
273
|
+
let newToken;
|
|
274
|
+
if (newFailCount >= FAILURE_ROTATION_THRESHOLD) {
|
|
275
|
+
newToken = generateToken();
|
|
276
|
+
await db.tenantDomain.update({
|
|
277
|
+
where: { id: domainId },
|
|
278
|
+
data: {
|
|
279
|
+
verificationToken: newToken,
|
|
280
|
+
tokenExpiresAt: tokenExpiresAt(),
|
|
281
|
+
verifyAttempts: 0,
|
|
282
|
+
},
|
|
283
|
+
});
|
|
284
|
+
await env.RATE_LIMIT_KV.delete(failKey);
|
|
285
|
+
rotated = true;
|
|
286
|
+
}
|
|
287
|
+
const reason = dnsResult.reason;
|
|
288
|
+
const messages = {
|
|
289
|
+
TOKEN_MISMATCH: "TXT record found but token did not match",
|
|
290
|
+
NO_RECORDS: "No TXT record found at the verification hostname",
|
|
291
|
+
DNS_ERROR: "DNS lookup failed — please try again later",
|
|
292
|
+
};
|
|
293
|
+
const body = {
|
|
294
|
+
error: "VERIFICATION_FAILED",
|
|
295
|
+
message: messages[reason] ?? "DNS verification failed",
|
|
296
|
+
remediation: `Add TXT record: _skybber-verify.${record.domain} = skybber-verify=${record.verificationToken}`,
|
|
297
|
+
};
|
|
298
|
+
if (rotated && newToken) {
|
|
299
|
+
body.tokenRotated = true;
|
|
300
|
+
body.remediation = `Token rotated after repeated failures. Add TXT record: _skybber-verify.${record.domain} = skybber-verify=${newToken}`;
|
|
301
|
+
}
|
|
302
|
+
return json(body, 422);
|
|
303
|
+
}
|
|
304
|
+
// ─── Internal helpers ───────────────────────────────────────────────────────
|
|
305
|
+
async checkAndIncrementRateLimit(env, key, maxAttempts, windowSeconds) {
|
|
306
|
+
const raw = await env.RATE_LIMIT_KV.get(key);
|
|
307
|
+
const current = raw ? parseInt(raw, 10) : 0;
|
|
308
|
+
if (current >= maxAttempts) {
|
|
309
|
+
return { limited: true, retryAfter: windowSeconds };
|
|
310
|
+
}
|
|
311
|
+
await env.RATE_LIMIT_KV.put(key, String(current + 1), {
|
|
312
|
+
expirationTtl: windowSeconds,
|
|
313
|
+
});
|
|
314
|
+
return { limited: false };
|
|
315
|
+
}
|
|
316
|
+
async incrementFailureCount(env, key, windowSeconds) {
|
|
317
|
+
const raw = await env.RATE_LIMIT_KV.get(key);
|
|
318
|
+
const current = raw ? parseInt(raw, 10) : 0;
|
|
319
|
+
const next = current + 1;
|
|
320
|
+
await env.RATE_LIMIT_KV.put(key, String(next), {
|
|
321
|
+
expirationTtl: windowSeconds,
|
|
322
|
+
});
|
|
323
|
+
return next;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
exports.DomainHandler = DomainHandler;
|
|
327
|
+
function formatDomainRecord(r) {
|
|
328
|
+
return {
|
|
329
|
+
id: r.id,
|
|
330
|
+
tenantId: r.tenantId,
|
|
331
|
+
domain: r.domain,
|
|
332
|
+
verifiedAt: r.verifiedAt,
|
|
333
|
+
tokenExpiresAt: r.tokenExpiresAt,
|
|
334
|
+
verifyAttempts: r.verifyAttempts,
|
|
335
|
+
createdAt: r.createdAt,
|
|
336
|
+
// Only include token for unverified records so admin knows the TXT value to set.
|
|
337
|
+
...(r.verifiedAt ? {} : {
|
|
338
|
+
verificationToken: r.verificationToken,
|
|
339
|
+
txtRecord: `skybber-verify=${r.verificationToken}`,
|
|
340
|
+
txtHost: `_skybber-verify.${r.domain}`,
|
|
341
|
+
}),
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
//# sourceMappingURL=domain-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domain-handler.js","sourceRoot":"","sources":["../../../src/lib/tenant/domain-handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain Validator
|
|
3
|
+
*
|
|
4
|
+
* Validates domain strings for tenant domain claims:
|
|
5
|
+
* - Format: valid hostname, no protocol, no path, lowercase
|
|
6
|
+
* - PSL check: rejects registrable-domain roots and public suffixes themselves
|
|
7
|
+
* - Skybber namespace: rejects *.skybber.com sub-claims
|
|
8
|
+
*
|
|
9
|
+
* Uses a bundled PSL snapshot to avoid runtime network fetches.
|
|
10
|
+
*/
|
|
11
|
+
export interface DomainValidationResult {
|
|
12
|
+
ok: true;
|
|
13
|
+
domain: string;
|
|
14
|
+
}
|
|
15
|
+
export interface DomainValidationError {
|
|
16
|
+
ok: false;
|
|
17
|
+
code: string;
|
|
18
|
+
message: string;
|
|
19
|
+
}
|
|
20
|
+
export type DomainValidationOutcome = DomainValidationResult | DomainValidationError;
|
|
21
|
+
/**
|
|
22
|
+
* Validates and normalises a domain string for tenant claiming.
|
|
23
|
+
*
|
|
24
|
+
* Returns `{ ok: true, domain }` with the normalised domain on success,
|
|
25
|
+
* or `{ ok: false, code, message }` with a rejection reason on failure.
|
|
26
|
+
*/
|
|
27
|
+
export declare function validateDomain(raw: string): DomainValidationOutcome;
|
|
28
|
+
//# sourceMappingURL=domain-validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domain-validator.d.ts","sourceRoot":"","sources":["../../../src/lib/tenant/domain-validator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAkGH,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,IAAI,CAAC;IACT,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,KAAK,CAAC;IACV,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,uBAAuB,GAAG,sBAAsB,GAAG,qBAAqB,CAAC;AAIrF;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,uBAAuB,CAoDnE"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Domain Validator
|
|
4
|
+
*
|
|
5
|
+
* Validates domain strings for tenant domain claims:
|
|
6
|
+
* - Format: valid hostname, no protocol, no path, lowercase
|
|
7
|
+
* - PSL check: rejects registrable-domain roots and public suffixes themselves
|
|
8
|
+
* - Skybber namespace: rejects *.skybber.com sub-claims
|
|
9
|
+
*
|
|
10
|
+
* Uses a bundled PSL snapshot to avoid runtime network fetches.
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.validateDomain = validateDomain;
|
|
14
|
+
const node_fs_1 = require("node:fs");
|
|
15
|
+
const node_path_1 = require("node:path");
|
|
16
|
+
let pslCache = null;
|
|
17
|
+
function loadPsl() {
|
|
18
|
+
if (pslCache)
|
|
19
|
+
return pslCache;
|
|
20
|
+
const snapshotPath = (0, node_path_1.join)(__dirname, "psl-snapshot.txt");
|
|
21
|
+
const lines = (0, node_fs_1.readFileSync)(snapshotPath, "utf-8").split("\n");
|
|
22
|
+
const exact = new Set();
|
|
23
|
+
const wildcards = new Set();
|
|
24
|
+
const exceptions = new Set();
|
|
25
|
+
for (const raw of lines) {
|
|
26
|
+
const line = raw.trim();
|
|
27
|
+
if (!line || line.startsWith("//"))
|
|
28
|
+
continue;
|
|
29
|
+
if (line.startsWith("!")) {
|
|
30
|
+
exceptions.add(line.slice(1).toLowerCase());
|
|
31
|
+
}
|
|
32
|
+
else if (line.startsWith("*.")) {
|
|
33
|
+
wildcards.add(line.slice(2).toLowerCase());
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
exact.add(line.toLowerCase());
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
pslCache = { exact, wildcards, exceptions };
|
|
40
|
+
return pslCache;
|
|
41
|
+
}
|
|
42
|
+
// ─── Public suffix check ─────────────────────────────────────────────────────
|
|
43
|
+
/**
|
|
44
|
+
* Returns true when `domain` is itself a public suffix or would be "owned"
|
|
45
|
+
* by a wildcard rule (i.e. not enough labels to form a registrable domain).
|
|
46
|
+
*
|
|
47
|
+
* Examples:
|
|
48
|
+
* isPublicSuffix("com") → true
|
|
49
|
+
* isPublicSuffix("co.uk") → true
|
|
50
|
+
* isPublicSuffix("example.com") → false (has a registrable label)
|
|
51
|
+
* isPublicSuffix("foo.*.ck") → false (exception *.ck: !www.ck applies)
|
|
52
|
+
*/
|
|
53
|
+
function isPublicSuffix(domain) {
|
|
54
|
+
const psl = loadPsl();
|
|
55
|
+
const labels = domain.split(".");
|
|
56
|
+
if (psl.exact.has(domain))
|
|
57
|
+
return true;
|
|
58
|
+
// Check wildcard: if parent suffix matches a wildcard rule, this domain is
|
|
59
|
+
// itself just the minimum-registrable level — only subdomains of it are
|
|
60
|
+
// "registrable" beyond the PSL boundary.
|
|
61
|
+
if (labels.length >= 2) {
|
|
62
|
+
const parent = labels.slice(1).join(".");
|
|
63
|
+
if (psl.wildcards.has(parent)) {
|
|
64
|
+
if (!psl.exceptions.has(domain))
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
// ─── Basic hostname format check ─────────────────────────────────────────────
|
|
71
|
+
const LABEL_RE = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
72
|
+
const MAX_DOMAIN_LENGTH = 253;
|
|
73
|
+
function isValidHostname(domain) {
|
|
74
|
+
if (!domain || domain.length > MAX_DOMAIN_LENGTH)
|
|
75
|
+
return false;
|
|
76
|
+
if (domain.startsWith("-") || domain.endsWith("-"))
|
|
77
|
+
return false;
|
|
78
|
+
if (domain.startsWith(".") || domain.endsWith("."))
|
|
79
|
+
return false;
|
|
80
|
+
const labels = domain.split(".");
|
|
81
|
+
if (labels.length < 2)
|
|
82
|
+
return false;
|
|
83
|
+
for (const label of labels) {
|
|
84
|
+
if (!LABEL_RE.test(label))
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
const tld = labels[labels.length - 1];
|
|
88
|
+
if (!tld || /^\d+$/.test(tld))
|
|
89
|
+
return false;
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
const SKYBBER_SUFFIX = ".skybber.com";
|
|
93
|
+
/**
|
|
94
|
+
* Validates and normalises a domain string for tenant claiming.
|
|
95
|
+
*
|
|
96
|
+
* Returns `{ ok: true, domain }` with the normalised domain on success,
|
|
97
|
+
* or `{ ok: false, code, message }` with a rejection reason on failure.
|
|
98
|
+
*/
|
|
99
|
+
function validateDomain(raw) {
|
|
100
|
+
if (typeof raw !== "string" || !raw) {
|
|
101
|
+
return { ok: false, code: "INVALID_DOMAIN", message: "Domain must be a non-empty string" };
|
|
102
|
+
}
|
|
103
|
+
// Strip leading/trailing whitespace and convert to lowercase.
|
|
104
|
+
// Handle IDN: Node's URL parser supports punycode implicitly via WHATWG URL.
|
|
105
|
+
let domain = raw.trim().toLowerCase();
|
|
106
|
+
// Reject if it looks like a URL.
|
|
107
|
+
if (domain.includes("://") || domain.includes("/") || domain.includes("?")) {
|
|
108
|
+
return { ok: false, code: "INVALID_DOMAIN", message: "Domain must not include a protocol or path" };
|
|
109
|
+
}
|
|
110
|
+
// Strip port if present (e.g. "example.com:8080").
|
|
111
|
+
const colonIdx = domain.indexOf(":");
|
|
112
|
+
if (colonIdx !== -1) {
|
|
113
|
+
domain = domain.slice(0, colonIdx);
|
|
114
|
+
}
|
|
115
|
+
// Attempt IDN normalisation via WHATWG URL.
|
|
116
|
+
try {
|
|
117
|
+
const url = new URL(`http://${domain}`);
|
|
118
|
+
domain = url.hostname;
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
return { ok: false, code: "INVALID_DOMAIN", message: "Domain is not a valid hostname" };
|
|
122
|
+
}
|
|
123
|
+
// Check for public suffix before hostname validation so single-label TLDs
|
|
124
|
+
// ("com", "net") return PUBLIC_SUFFIX rather than INVALID_DOMAIN.
|
|
125
|
+
if (isPublicSuffix(domain)) {
|
|
126
|
+
return {
|
|
127
|
+
ok: false,
|
|
128
|
+
code: "PUBLIC_SUFFIX",
|
|
129
|
+
message: "Cannot claim a public suffix domain (e.g. com, co.uk, gmail.com is fine but co.uk is not)",
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
if (!isValidHostname(domain)) {
|
|
133
|
+
return { ok: false, code: "INVALID_DOMAIN", message: "Domain is not a valid hostname" };
|
|
134
|
+
}
|
|
135
|
+
// Reject *.skybber.com sub-claims.
|
|
136
|
+
if (domain.endsWith(SKYBBER_SUFFIX) || domain === "skybber.com") {
|
|
137
|
+
return {
|
|
138
|
+
ok: false,
|
|
139
|
+
code: "RESERVED_DOMAIN",
|
|
140
|
+
message: "Cannot claim a skybber.com subdomain",
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
return { ok: true, domain };
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=domain-validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domain-validator.js","sourceRoot":"","sources":["../../../src/lib/tenant/domain-validator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain Verifier
|
|
3
|
+
*
|
|
4
|
+
* Performs DNS TXT record lookups to confirm that a tenant controls a domain.
|
|
5
|
+
*
|
|
6
|
+
* Protocol:
|
|
7
|
+
* 1. Tenant claims a domain → receives a token.
|
|
8
|
+
* 2. Tenant adds TXT record: `_skybber-verify.{domain}` = `skybber-verify={token}`
|
|
9
|
+
* 3. POST …/verify → this module resolves the TXT record and checks for the token.
|
|
10
|
+
*
|
|
11
|
+
* Fail-closed: any DNS error (NXDOMAIN, timeout, network failure) returns
|
|
12
|
+
* { verified: false, reason: "DNS_ERROR" }. The token must match exactly.
|
|
13
|
+
*/
|
|
14
|
+
export interface VerifyResult {
|
|
15
|
+
verified: true;
|
|
16
|
+
}
|
|
17
|
+
export interface VerifyFailure {
|
|
18
|
+
verified: false;
|
|
19
|
+
reason: "TOKEN_MISMATCH" | "NO_RECORDS" | "DNS_ERROR";
|
|
20
|
+
}
|
|
21
|
+
export type DnsVerifyOutcome = VerifyResult | VerifyFailure;
|
|
22
|
+
/**
|
|
23
|
+
* Resolves the TXT record at `_skybber-verify.{domain}` and checks whether
|
|
24
|
+
* any value equals `skybber-verify={token}` exactly.
|
|
25
|
+
*
|
|
26
|
+
* Uses `dns.promises.resolveTxt` which does not follow HTTP redirects —
|
|
27
|
+
* there is no SSRF vector here.
|
|
28
|
+
*/
|
|
29
|
+
export declare function verifyDomainToken(domain: string, token: string): Promise<DnsVerifyOutcome>;
|
|
30
|
+
//# sourceMappingURL=domain-verifier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domain-verifier.d.ts","sourceRoot":"","sources":["../../../src/lib/tenant/domain-verifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,IAAI,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,KAAK,CAAC;IAChB,MAAM,EAAE,gBAAgB,GAAG,YAAY,GAAG,WAAW,CAAC;CACvD;AAED,MAAM,MAAM,gBAAgB,GAAG,YAAY,GAAG,aAAa,CAAC;AAI5D;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,gBAAgB,CAAC,CA6B3B"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Domain Verifier
|
|
4
|
+
*
|
|
5
|
+
* Performs DNS TXT record lookups to confirm that a tenant controls a domain.
|
|
6
|
+
*
|
|
7
|
+
* Protocol:
|
|
8
|
+
* 1. Tenant claims a domain → receives a token.
|
|
9
|
+
* 2. Tenant adds TXT record: `_skybber-verify.{domain}` = `skybber-verify={token}`
|
|
10
|
+
* 3. POST …/verify → this module resolves the TXT record and checks for the token.
|
|
11
|
+
*
|
|
12
|
+
* Fail-closed: any DNS error (NXDOMAIN, timeout, network failure) returns
|
|
13
|
+
* { verified: false, reason: "DNS_ERROR" }. The token must match exactly.
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.verifyDomainToken = verifyDomainToken;
|
|
17
|
+
const node_dns_1 = require("node:dns");
|
|
18
|
+
const TXT_PREFIX = "_skybber-verify.";
|
|
19
|
+
/**
|
|
20
|
+
* Resolves the TXT record at `_skybber-verify.{domain}` and checks whether
|
|
21
|
+
* any value equals `skybber-verify={token}` exactly.
|
|
22
|
+
*
|
|
23
|
+
* Uses `dns.promises.resolveTxt` which does not follow HTTP redirects —
|
|
24
|
+
* there is no SSRF vector here.
|
|
25
|
+
*/
|
|
26
|
+
async function verifyDomainToken(domain, token) {
|
|
27
|
+
const lookupName = `${TXT_PREFIX}${domain}`;
|
|
28
|
+
const expected = `skybber-verify=${token}`;
|
|
29
|
+
let txtRecords;
|
|
30
|
+
try {
|
|
31
|
+
txtRecords = await node_dns_1.promises.resolveTxt(lookupName);
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
const code = err.code;
|
|
35
|
+
if (code === "ENODATA" || code === "ENOTFOUND") {
|
|
36
|
+
return { verified: false, reason: "NO_RECORDS" };
|
|
37
|
+
}
|
|
38
|
+
return { verified: false, reason: "DNS_ERROR" };
|
|
39
|
+
}
|
|
40
|
+
if (!txtRecords || txtRecords.length === 0) {
|
|
41
|
+
return { verified: false, reason: "NO_RECORDS" };
|
|
42
|
+
}
|
|
43
|
+
// TXT records are returned as arrays of strings (chunks). Join each record's
|
|
44
|
+
// chunks and compare against the expected value.
|
|
45
|
+
for (const chunks of txtRecords) {
|
|
46
|
+
const value = chunks.join("");
|
|
47
|
+
if (value === expected) {
|
|
48
|
+
return { verified: true };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return { verified: false, reason: "TOKEN_MISMATCH" };
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=domain-verifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domain-verifier.js","sourceRoot":"","sources":["../../../src/lib/tenant/domain-verifier.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;GAYG;;AAwBH,8CAgCC;AAtDD,uCAA2C;AAa3C,MAAM,UAAU,GAAG,kBAAkB,CAAC;AAEtC;;;;;;GAMG;AACI,KAAK,UAAU,iBAAiB,CACrC,MAAc,EACd,KAAa;IAEb,MAAM,UAAU,GAAG,GAAG,UAAU,GAAG,MAAM,EAAE,CAAC;IAC5C,MAAM,QAAQ,GAAG,kBAAkB,KAAK,EAAE,CAAC;IAE3C,IAAI,UAAsB,CAAC;IAC3B,IAAI,CAAC;QACH,UAAU,GAAG,MAAM,mBAAG,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;IAChD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,GAAI,GAA6B,CAAC,IAAI,CAAC;QACjD,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,WAAW,EAAE,CAAC;YAC/C,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;QACnD,CAAC;QACD,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;IAClD,CAAC;IAED,IAAI,CAAC,UAAU,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3C,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;IACnD,CAAC;IAED,6EAA6E;IAC7E,iDAAiD;IACjD,KAAK,MAAM,MAAM,IAAI,UAAU,EAAE,CAAC;QAChC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC9B,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;YACvB,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;AACvD,CAAC"}
|