@bajustone/fortress 1.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1011 -0
- package/LICENSE +21 -0
- package/README.md +760 -0
- package/SECURITY.md +197 -0
- package/bin/fortress.ts +667 -0
- package/dist/auth-service-BcyQ2PuZ.d.cts +307 -0
- package/dist/auth-service-BkzZkWfH.d.ts +307 -0
- package/dist/config-B2366s_G.d.ts +665 -0
- package/dist/config-GtlVt6ZD.d.cts +665 -0
- package/dist/convert-routes-BLCQT57f.d.ts +72 -0
- package/dist/convert-routes-BdMvP2_X.d.cts +72 -0
- package/dist/crypto.cjs +61 -0
- package/dist/crypto.d.cts +36 -0
- package/dist/crypto.d.ts +36 -0
- package/dist/crypto.js +35 -0
- package/dist/drift-BT1wtMDJ.d.cts +22 -0
- package/dist/drift-k9LiYpRP.d.ts +22 -0
- package/dist/drizzle/pg.cjs +481 -0
- package/dist/drizzle/pg.d.cts +15 -0
- package/dist/drizzle/pg.d.ts +15 -0
- package/dist/drizzle/pg.js +454 -0
- package/dist/drizzle.cjs +1442 -0
- package/dist/drizzle.d.cts +85 -0
- package/dist/drizzle.d.ts +85 -0
- package/dist/drizzle.js +1411 -0
- package/dist/express.cjs +1357 -0
- package/dist/express.d.cts +333 -0
- package/dist/express.d.ts +333 -0
- package/dist/express.js +1314 -0
- package/dist/fetcher.cjs +77 -0
- package/dist/fetcher.d.cts +7 -0
- package/dist/fetcher.d.ts +7 -0
- package/dist/fetcher.js +41 -0
- package/dist/fortress-BmSvFfqK.d.cts +1089 -0
- package/dist/fortress-uA-_cheR.d.ts +1089 -0
- package/dist/hono.cjs +1530 -0
- package/dist/hono.d.cts +514 -0
- package/dist/hono.d.ts +514 -0
- package/dist/hono.js +1483 -0
- package/dist/index-CxEkfCA0.d.cts +77 -0
- package/dist/index-CxEkfCA0.d.ts +77 -0
- package/dist/index-D054pcb-.d.cts +146 -0
- package/dist/index-jAMbbUAY.d.ts +146 -0
- package/dist/index.cjs +9046 -0
- package/dist/index.d.cts +717 -0
- package/dist/index.d.ts +717 -0
- package/dist/index.js +8935 -0
- package/dist/jwt.cjs +255 -0
- package/dist/jwt.d.cts +90 -0
- package/dist/jwt.d.ts +90 -0
- package/dist/jwt.js +227 -0
- package/dist/otel.cjs +108 -0
- package/dist/otel.d.cts +62 -0
- package/dist/otel.d.ts +62 -0
- package/dist/otel.js +73 -0
- package/dist/plugins/account-lockout.cjs +322 -0
- package/dist/plugins/account-lockout.d.cts +42 -0
- package/dist/plugins/account-lockout.d.ts +42 -0
- package/dist/plugins/account-lockout.js +295 -0
- package/dist/plugins/admin.cjs +2378 -0
- package/dist/plugins/admin.d.cts +72 -0
- package/dist/plugins/admin.d.ts +72 -0
- package/dist/plugins/admin.js +2351 -0
- package/dist/plugins/api-key.cjs +792 -0
- package/dist/plugins/api-key.d.cts +121 -0
- package/dist/plugins/api-key.d.ts +121 -0
- package/dist/plugins/api-key.js +765 -0
- package/dist/plugins/audit-log.cjs +493 -0
- package/dist/plugins/audit-log.d.cts +86 -0
- package/dist/plugins/audit-log.d.ts +86 -0
- package/dist/plugins/audit-log.js +468 -0
- package/dist/plugins/data-isolation.cjs +211 -0
- package/dist/plugins/data-isolation.d.cts +49 -0
- package/dist/plugins/data-isolation.d.ts +49 -0
- package/dist/plugins/data-isolation.js +186 -0
- package/dist/plugins/email-verification.cjs +273 -0
- package/dist/plugins/email-verification.d.cts +44 -0
- package/dist/plugins/email-verification.d.ts +44 -0
- package/dist/plugins/email-verification.js +246 -0
- package/dist/plugins/magic-link.cjs +231 -0
- package/dist/plugins/magic-link.d.cts +39 -0
- package/dist/plugins/magic-link.d.ts +39 -0
- package/dist/plugins/magic-link.js +204 -0
- package/dist/plugins/oauth.cjs +1696 -0
- package/dist/plugins/oauth.d.cts +406 -0
- package/dist/plugins/oauth.d.ts +406 -0
- package/dist/plugins/oauth.js +1669 -0
- package/dist/plugins/openapi.cjs +1185 -0
- package/dist/plugins/openapi.d.cts +6 -0
- package/dist/plugins/openapi.d.ts +6 -0
- package/dist/plugins/openapi.js +1158 -0
- package/dist/plugins/rate-limit/express.cjs +55 -0
- package/dist/plugins/rate-limit/express.d.cts +64 -0
- package/dist/plugins/rate-limit/express.d.ts +64 -0
- package/dist/plugins/rate-limit/express.js +30 -0
- package/dist/plugins/rate-limit/hono.cjs +51 -0
- package/dist/plugins/rate-limit/hono.d.cts +59 -0
- package/dist/plugins/rate-limit/hono.d.ts +59 -0
- package/dist/plugins/rate-limit/hono.js +26 -0
- package/dist/plugins/rate-limit/sveltekit.cjs +49 -0
- package/dist/plugins/rate-limit/sveltekit.d.cts +59 -0
- package/dist/plugins/rate-limit/sveltekit.d.ts +59 -0
- package/dist/plugins/rate-limit/sveltekit.js +24 -0
- package/dist/plugins/rate-limit.cjs +354 -0
- package/dist/plugins/rate-limit.d.cts +140 -0
- package/dist/plugins/rate-limit.d.ts +140 -0
- package/dist/plugins/rate-limit.js +325 -0
- package/dist/plugins/social-login.cjs +905 -0
- package/dist/plugins/social-login.d.cts +114 -0
- package/dist/plugins/social-login.d.ts +114 -0
- package/dist/plugins/social-login.js +880 -0
- package/dist/plugins/tenancy.cjs +780 -0
- package/dist/plugins/tenancy.d.cts +108 -0
- package/dist/plugins/tenancy.d.ts +108 -0
- package/dist/plugins/tenancy.js +753 -0
- package/dist/plugins/two-factor.cjs +555 -0
- package/dist/plugins/two-factor.d.cts +67 -0
- package/dist/plugins/two-factor.d.ts +67 -0
- package/dist/plugins/two-factor.js +526 -0
- package/dist/plugins/webauthn.cjs +786 -0
- package/dist/plugins/webauthn.d.cts +72 -0
- package/dist/plugins/webauthn.d.ts +72 -0
- package/dist/plugins/webauthn.js +766 -0
- package/dist/plugins/webhook.cjs +819 -0
- package/dist/plugins/webhook.d.cts +254 -0
- package/dist/plugins/webhook.d.ts +254 -0
- package/dist/plugins/webhook.js +789 -0
- package/dist/protect-D1v_0upK.d.cts +123 -0
- package/dist/protect-DsxV_-dJ.d.ts +123 -0
- package/dist/sveltekit.cjs +1258 -0
- package/dist/sveltekit.d.cts +420 -0
- package/dist/sveltekit.d.ts +420 -0
- package/dist/sveltekit.js +1207 -0
- package/dist/testing.cjs +4294 -0
- package/dist/testing.d.cts +197 -0
- package/dist/testing.d.ts +197 -0
- package/dist/testing.js +4264 -0
- package/dist/types-et0R8tsC.d.cts +217 -0
- package/dist/types-et0R8tsC.d.ts +217 -0
- package/docs/adapter-typed-helpers.md +192 -0
- package/docs/admin-recipes.md +227 -0
- package/docs/architecture.md +434 -0
- package/docs/ci/github-actions.yml +59 -0
- package/docs/ci.md +109 -0
- package/docs/compatibility.md +115 -0
- package/docs/deployment.md +305 -0
- package/docs/hardening.md +118 -0
- package/docs/host-owned-routes.md +154 -0
- package/docs/migrations/0001-schema-version.md +54 -0
- package/docs/migrations/0002-initial-schema.md +84 -0
- package/docs/migrations/upgrade-guide.md +160 -0
- package/docs/observability.md +394 -0
- package/docs/plugins/account-lockout.md +131 -0
- package/docs/plugins/admin.md +402 -0
- package/docs/plugins/api-key.md +327 -0
- package/docs/plugins/audit-log.md +261 -0
- package/docs/plugins/data-isolation.md +186 -0
- package/docs/plugins/email-verification.md +121 -0
- package/docs/plugins/magic-link.md +123 -0
- package/docs/plugins/oauth.md +544 -0
- package/docs/plugins/openapi.md +250 -0
- package/docs/plugins/rate-limit.md +223 -0
- package/docs/plugins/social-login.md +337 -0
- package/docs/plugins/tenancy.md +122 -0
- package/docs/plugins/two-factor.md +191 -0
- package/docs/plugins/webauthn.md +188 -0
- package/docs/plugins/webhook.md +242 -0
- package/docs/policy-as-code.md +156 -0
- package/docs/route-manifest.md +46 -0
- package/docs/security.md +261 -0
- package/docs/threat-model.md +161 -0
- package/examples/README.md +119 -0
- package/examples/express-app/index.ts +747 -0
- package/examples/hono-app/docker-compose.yml +14 -0
- package/examples/hono-app/index.ts +1009 -0
- package/examples/policy/fortress.policy.json +47 -0
- package/examples/sveltekit-app/README.md +125 -0
- package/examples/sveltekit-app/src/app.d.ts +16 -0
- package/examples/sveltekit-app/src/hooks.server.ts +23 -0
- package/examples/sveltekit-app/src/lib/server/fortress.ts +81 -0
- package/examples/sveltekit-app/src/routes/api/echo/[id]/+server.ts +32 -0
- package/examples/sveltekit-app/src/routes/api/fortress/[...path]/+server.ts +15 -0
- package/examples/sveltekit-app/src/routes/dashboard/+page.server.ts +20 -0
- package/examples/sveltekit-app/src/routes/login/+page.server.ts +48 -0
- package/examples/sveltekit-app/src/routes/oauth/consent/+page.server.ts +69 -0
- package/examples/sveltekit-app/src/routes/oauth/consent/+page.svelte +83 -0
- package/migrations/pg/0001_schema_version.down.sql +2 -0
- package/migrations/pg/0001_schema_version.sql +8 -0
- package/migrations/pg/0002_initial_schema.down.sql +36 -0
- package/migrations/pg/0002_initial_schema.sql +376 -0
- package/migrations/pg/0003_auth_continuation.down.sql +6 -0
- package/migrations/pg/0003_auth_continuation.sql +30 -0
- package/migrations/pg/0004_tenant_default_unique.down.sql +1 -0
- package/migrations/pg/0004_tenant_default_unique.sql +14 -0
- package/migrations/pg/0005_hot_indexes_timestamptz.down.sql +71 -0
- package/migrations/pg/0005_hot_indexes_timestamptz.sql +71 -0
- package/migrations/pg/0006_canonical_email.down.sql +3 -0
- package/migrations/pg/0006_canonical_email.sql +8 -0
- package/migrations/pg/0007_audit_chain_anchor.down.sql +1 -0
- package/migrations/pg/0007_audit_chain_anchor.sql +8 -0
- package/migrations/pg/0008_two_factor_hardening.down.sql +8 -0
- package/migrations/pg/0008_two_factor_hardening.sql +8 -0
- package/migrations/pg/0009_encrypt_totp_secrets.down.sql +2 -0
- package/migrations/pg/0009_encrypt_totp_secrets.sql +5 -0
- package/migrations/pg/0010_bigint_append_only_ids.down.sql +2 -0
- package/migrations/pg/0010_bigint_append_only_ids.sql +23 -0
- package/migrations/sqlite/0001_schema_version.down.sql +2 -0
- package/migrations/sqlite/0001_schema_version.sql +8 -0
- package/migrations/sqlite/0002_initial_schema.down.sql +36 -0
- package/migrations/sqlite/0002_initial_schema.sql +376 -0
- package/migrations/sqlite/0003_auth_continuation.down.sql +27 -0
- package/migrations/sqlite/0003_auth_continuation.sql +45 -0
- package/migrations/sqlite/0004_tenant_default_unique.down.sql +1 -0
- package/migrations/sqlite/0004_tenant_default_unique.sql +13 -0
- package/migrations/sqlite/0005_hot_indexes_timestamptz.down.sql +10 -0
- package/migrations/sqlite/0005_hot_indexes_timestamptz.sql +10 -0
- package/migrations/sqlite/0006_canonical_email.down.sql +3 -0
- package/migrations/sqlite/0006_canonical_email.sql +8 -0
- package/migrations/sqlite/0007_audit_chain_anchor.down.sql +1 -0
- package/migrations/sqlite/0007_audit_chain_anchor.sql +8 -0
- package/migrations/sqlite/0008_two_factor_hardening.down.sql +14 -0
- package/migrations/sqlite/0008_two_factor_hardening.sql +7 -0
- package/migrations/sqlite/0009_encrypt_totp_secrets.down.sql +2 -0
- package/migrations/sqlite/0009_encrypt_totp_secrets.sql +5 -0
- package/migrations/sqlite/0010_bigint_append_only_ids.down.sql +1 -0
- package/migrations/sqlite/0010_bigint_append_only_ids.sql +2 -0
- package/package.json +398 -0
- package/src/adapters/database/index.ts +63 -0
- package/src/adapters/database/types.ts +22 -0
- package/src/changelog-script.test.ts +21 -0
- package/src/cli.test.ts +79 -0
- package/src/core/auth/auth-admin.test.ts +247 -0
- package/src/core/auth/auth-endpoints.ts +558 -0
- package/src/core/auth/auth-observer.test.ts +191 -0
- package/src/core/auth/auth-service.ts +1464 -0
- package/src/core/auth/email.test.ts +17 -0
- package/src/core/auth/email.ts +11 -0
- package/src/core/auth/hooks.test.ts +251 -0
- package/src/core/auth/impersonation.test.ts +179 -0
- package/src/core/auth/jwt.test.ts +184 -0
- package/src/core/auth/jwt.ts +239 -0
- package/src/core/auth/login-timing.test.ts +77 -0
- package/src/core/auth/password-policy.test.ts +253 -0
- package/src/core/auth/password-policy.ts +220 -0
- package/src/core/auth/password.test.ts +42 -0
- package/src/core/auth/password.ts +62 -0
- package/src/core/auth/post-auth-gate.test.ts +258 -0
- package/src/core/auth/post-auth-gate.ts +228 -0
- package/src/core/auth/refresh-token.test.ts +86 -0
- package/src/core/auth/refresh-token.ts +105 -0
- package/src/core/auth/timing-safe.ts +23 -0
- package/src/core/config.ts +212 -0
- package/src/core/endpoint.ts +149 -0
- package/src/core/errors.ts +199 -0
- package/src/core/fetcher-interop.test.ts +106 -0
- package/src/core/fortress.test.ts +354 -0
- package/src/core/fortress.ts +550 -0
- package/src/core/http/call.test.ts +148 -0
- package/src/core/http/call.ts +149 -0
- package/src/core/http/cookie-serialize.test.ts +198 -0
- package/src/core/http/cookie-serialize.ts +107 -0
- package/src/core/http/csrf.test.ts +140 -0
- package/src/core/http/csrf.ts +173 -0
- package/src/core/http/dispatch.ts +652 -0
- package/src/core/http/error-response.test.ts +69 -0
- package/src/core/http/error-response.ts +93 -0
- package/src/core/http/fortress-rbac.test.ts +43 -0
- package/src/core/http/fortress-rbac.ts +127 -0
- package/src/core/http/handle-request.test.ts +441 -0
- package/src/core/http/handle-request.ts +354 -0
- package/src/core/http/match.test.ts +122 -0
- package/src/core/http/match.ts +126 -0
- package/src/core/http/outbound.test.ts +34 -0
- package/src/core/http/outbound.ts +105 -0
- package/src/core/http/plugin-middleware.ts +67 -0
- package/src/core/http/principal.test.ts +345 -0
- package/src/core/http/principal.ts +86 -0
- package/src/core/http/protect.test.ts +220 -0
- package/src/core/http/protect.ts +394 -0
- package/src/core/http/token-extraction.test.ts +59 -0
- package/src/core/http/token-extraction.ts +53 -0
- package/src/core/iam/explain.test.ts +177 -0
- package/src/core/iam/explain.ts +302 -0
- package/src/core/iam/iam-endpoints.ts +779 -0
- package/src/core/iam/iam-service.test.ts +829 -0
- package/src/core/iam/iam-service.ts +1137 -0
- package/src/core/iam/permission-cache.test.ts +115 -0
- package/src/core/iam/permission-cache.ts +71 -0
- package/src/core/iam/permission-check-observer.test.ts +90 -0
- package/src/core/iam/permission-evaluator.test.ts +291 -0
- package/src/core/iam/permission-evaluator.ts +198 -0
- package/src/core/iam/permission-sync.test.ts +166 -0
- package/src/core/iam/permission-sync.ts +192 -0
- package/src/core/iam/resource-sync.test.ts +70 -0
- package/src/core/iam/resource-sync.ts +182 -0
- package/src/core/iam/service-account-http.test.ts +529 -0
- package/src/core/iam/service-account.test.ts +282 -0
- package/src/core/internal-adapter.test.ts +389 -0
- package/src/core/internal-adapter.ts +435 -0
- package/src/core/json-schema.ts +50 -0
- package/src/core/manifest/__snapshots__/route-manifest.test.ts.snap +192 -0
- package/src/core/manifest/drift.ts +103 -0
- package/src/core/manifest/route-manifest.test.ts +142 -0
- package/src/core/manifest/route-manifest.ts +154 -0
- package/src/core/migrate.test.ts +72 -0
- package/src/core/migrations/engine.test.ts +308 -0
- package/src/core/migrations/engine.ts +548 -0
- package/src/core/migrations/migrations.ts +1771 -0
- package/src/core/migrations/pg-migration.integration-test.ts +353 -0
- package/src/core/migrations/upgrade-fixture.test.ts +378 -0
- package/src/core/observability/db-instrumentation.ts +205 -0
- package/src/core/observability/listener-list.test.ts +124 -0
- package/src/core/observability/listener-list.ts +73 -0
- package/src/core/observability/logger.test.ts +43 -0
- package/src/core/observability/logger.ts +49 -0
- package/src/core/observability/spans.test.ts +193 -0
- package/src/core/observability/types.ts +80 -0
- package/src/core/openapi-drift.test.ts +41 -0
- package/src/core/openapi.ts +47 -0
- package/src/core/plugin-methods-map.ts +75 -0
- package/src/core/plugin-runner.test.ts +233 -0
- package/src/core/plugin-runner.ts +276 -0
- package/src/core/plugin.ts +210 -0
- package/src/core/policy/apply.ts +272 -0
- package/src/core/policy/diff.ts +288 -0
- package/src/core/policy/loader.test.ts +63 -0
- package/src/core/policy/loader.ts +214 -0
- package/src/core/policy/policy.test.ts +232 -0
- package/src/core/policy/types.ts +105 -0
- package/src/core/schema-builder.test.ts +660 -0
- package/src/core/schema-builder.ts +881 -0
- package/src/core/standard-schema.ts +56 -0
- package/src/core/types.ts +258 -0
- package/src/core/validation.test.ts +195 -0
- package/src/core/validation.ts +192 -0
- package/src/drizzle/adapter.test.ts +425 -0
- package/src/drizzle/adapter.ts +474 -0
- package/src/drizzle/index.ts +31 -0
- package/src/drizzle/pg/adapter.integration-test.ts +456 -0
- package/src/drizzle/pg/index.ts +13 -0
- package/src/drizzle/pg/pg.integration-test.ts +1539 -0
- package/src/drizzle/pg/schema.ts +539 -0
- package/src/drizzle/pg-error-map.test.ts +218 -0
- package/src/drizzle/pg-error-map.ts +151 -0
- package/src/drizzle/schema.ts +544 -0
- package/src/drizzle/sqlite-serialization.test.ts +106 -0
- package/src/express/express-api-key-user-routes.test.ts +241 -0
- package/src/express/express.test.ts +442 -0
- package/src/express/handle.ts +191 -0
- package/src/express/index.ts +62 -0
- package/src/express/middleware.ts +551 -0
- package/src/express/protect.ts +154 -0
- package/src/express/validated.test.ts +86 -0
- package/src/express/validated.ts +99 -0
- package/src/fetcher/index.ts +81 -0
- package/src/hono/convert-routes.test.ts +220 -0
- package/src/hono/convert-routes.ts +196 -0
- package/src/hono/converters.test.ts +50 -0
- package/src/hono/converters.ts +43 -0
- package/src/hono/handle.ts +79 -0
- package/src/hono/helpers.ts +2 -0
- package/src/hono/hono-api-key-user-routes.test.ts +225 -0
- package/src/hono/hono-handlers.test.ts +861 -0
- package/src/hono/hono.test.ts +454 -0
- package/src/hono/index.ts +101 -0
- package/src/hono/middleware/auth.ts +236 -0
- package/src/hono/middleware/auth.types.test.ts +94 -0
- package/src/hono/middleware/csrf.test.ts +127 -0
- package/src/hono/middleware/csrf.ts +68 -0
- package/src/hono/middleware/error-handler.ts +12 -0
- package/src/hono/middleware/plugin-middleware.ts +35 -0
- package/src/hono/middleware/rbac.ts +131 -0
- package/src/hono/middleware/security-headers.test.ts +88 -0
- package/src/hono/middleware/security-headers.ts +68 -0
- package/src/hono/openapi.ts +237 -0
- package/src/hono/protect.ts +87 -0
- package/src/hono/validated.test.ts +123 -0
- package/src/hono/validated.ts +62 -0
- package/src/index.ts +309 -0
- package/src/integration.test.ts +718 -0
- package/src/internal/changelog.ts +56 -0
- package/src/otel/index.ts +158 -0
- package/src/otel/otel-types.test.ts +35 -0
- package/src/otel/otel.test.ts +235 -0
- package/src/plugins/account-lockout/account-lockout.test.ts +367 -0
- package/src/plugins/account-lockout/index.ts +267 -0
- package/src/plugins/admin/admin-api-key.test.ts +438 -0
- package/src/plugins/admin/index.ts +1612 -0
- package/src/plugins/api-key/api-key.test.ts +684 -0
- package/src/plugins/api-key/core.ts +336 -0
- package/src/plugins/api-key/index.ts +315 -0
- package/src/plugins/audit-log/audit-log.test.ts +749 -0
- package/src/plugins/audit-log/index.ts +680 -0
- package/src/plugins/data-isolation/data-isolation.test.ts +197 -0
- package/src/plugins/data-isolation/index.ts +157 -0
- package/src/plugins/email-verification/email-verification.test.ts +199 -0
- package/src/plugins/email-verification/index.ts +190 -0
- package/src/plugins/magic-link/index.ts +129 -0
- package/src/plugins/magic-link/magic-link.test.ts +156 -0
- package/src/plugins/oauth/id-token.ts +86 -0
- package/src/plugins/oauth/index.ts +2145 -0
- package/src/plugins/oauth/jwks.test.ts +32 -0
- package/src/plugins/oauth/jwks.ts +182 -0
- package/src/plugins/oauth/oauth.test.ts +2220 -0
- package/src/plugins/oauth/pkce.ts +58 -0
- package/src/plugins/openapi/index.ts +298 -0
- package/src/plugins/openapi/openapi.test.ts +425 -0
- package/src/plugins/openapi/spec-builder.ts +287 -0
- package/src/plugins/rate-limit/express.test.ts +89 -0
- package/src/plugins/rate-limit/express.ts +86 -0
- package/src/plugins/rate-limit/hono.test.ts +60 -0
- package/src/plugins/rate-limit/hono.ts +74 -0
- package/src/plugins/rate-limit/index.ts +383 -0
- package/src/plugins/rate-limit/memory-store.ts +61 -0
- package/src/plugins/rate-limit/rate-limit.test.ts +756 -0
- package/src/plugins/rate-limit/sveltekit.test.ts +58 -0
- package/src/plugins/rate-limit/sveltekit.ts +66 -0
- package/src/plugins/social-login/index.ts +715 -0
- package/src/plugins/social-login/providers/apple.ts +34 -0
- package/src/plugins/social-login/providers/discord.ts +26 -0
- package/src/plugins/social-login/providers/github.ts +54 -0
- package/src/plugins/social-login/providers/google.ts +23 -0
- package/src/plugins/social-login/providers/index.ts +22 -0
- package/src/plugins/social-login/providers/microsoft.ts +35 -0
- package/src/plugins/social-login/providers/oidc.ts +37 -0
- package/src/plugins/social-login/providers/providers.test.ts +211 -0
- package/src/plugins/social-login/social-login.test.ts +675 -0
- package/src/plugins/social-login/types.ts +80 -0
- package/src/plugins/tenancy/index.ts +544 -0
- package/src/plugins/tenancy/tenancy.test.ts +323 -0
- package/src/plugins/two-factor/index.ts +569 -0
- package/src/plugins/two-factor/two-factor.test.ts +235 -0
- package/src/plugins/webauthn/index.ts +554 -0
- package/src/plugins/webauthn/webauthn.test.ts +472 -0
- package/src/plugins/webhook/builtin-events.ts +29 -0
- package/src/plugins/webhook/delivery.test.ts +51 -0
- package/src/plugins/webhook/delivery.ts +154 -0
- package/src/plugins/webhook/hooks.ts +89 -0
- package/src/plugins/webhook/index.ts +445 -0
- package/src/plugins/webhook/queue/database.ts +67 -0
- package/src/plugins/webhook/queue/in-memory.test.ts +48 -0
- package/src/plugins/webhook/queue/in-memory.ts +71 -0
- package/src/plugins/webhook/queue/types.ts +51 -0
- package/src/plugins/webhook/registry.test.ts +48 -0
- package/src/plugins/webhook/registry.ts +64 -0
- package/src/plugins/webhook/signing.ts +45 -0
- package/src/plugins/webhook/ssrf.ts +198 -0
- package/src/plugins/webhook/types.ts +138 -0
- package/src/plugins/webhook/webhook.test.ts +324 -0
- package/src/sveltekit/actions.ts +233 -0
- package/src/sveltekit/catch-all.ts +43 -0
- package/src/sveltekit/cookies.ts +136 -0
- package/src/sveltekit/handle.ts +356 -0
- package/src/sveltekit/helpers.ts +69 -0
- package/src/sveltekit/index.ts +74 -0
- package/src/sveltekit/protect.ts +80 -0
- package/src/sveltekit/sveltekit-types.test.ts +31 -0
- package/src/sveltekit/sveltekit.test.ts +799 -0
- package/src/sveltekit/types.ts +134 -0
- package/src/sveltekit/validated.test.ts +117 -0
- package/src/sveltekit/validated.ts +78 -0
- package/src/testing/adapter-conformance.test.ts +408 -0
- package/src/testing/checks.test.ts +124 -0
- package/src/testing/checks.ts +304 -0
- package/src/testing/index.ts +107 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { EndpointDefinition } from '../core/endpoint';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import { obj, str } from '../core/schema-builder';
|
|
4
|
+
import { convertRoutes } from './convert-routes';
|
|
5
|
+
import { fetcherSchemaConverter, identitySchemaConverter, toJSONSchemaConverter } from './converters';
|
|
6
|
+
import { buildRouteDefinition } from './openapi';
|
|
7
|
+
|
|
8
|
+
describe('hono OpenAPI converters (Zod-free defaults)', () => {
|
|
9
|
+
it('identitySchemaConverter passes JSON Schema through unchanged', () => {
|
|
10
|
+
const js = { type: 'object' as const, properties: { a: { type: 'string' as const } } };
|
|
11
|
+
expect(identitySchemaConverter(js)).toBe(js);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('fetcherSchemaConverter compiles JSON Schema into a validating Standard Schema', () => {
|
|
15
|
+
const schema = fetcherSchemaConverter({ type: 'object', properties: { a: { type: 'string' } }, required: ['a'] });
|
|
16
|
+
expect(typeof schema['~standard'].validate).toBe('function');
|
|
17
|
+
expect(schema['~standard'].validate({ a: 'x' })).toEqual({ value: { a: 'x' } });
|
|
18
|
+
const bad = schema['~standard'].validate({}) as { issues?: unknown };
|
|
19
|
+
expect(bad.issues).toBeTruthy(); // missing required `a`
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('toJSONSchemaConverter extracts JSON Schema from a fortress/fetcher builder schema', () => {
|
|
23
|
+
const js = toJSONSchemaConverter(obj({ a: str() }, 'a'));
|
|
24
|
+
expect(js.type).toBe('object');
|
|
25
|
+
expect(js.properties?.a?.type).toBe('string');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('mounting works with identitySchemaConverter (no Zod)', () => {
|
|
29
|
+
const ep: EndpointDefinition = {
|
|
30
|
+
method: 'POST',
|
|
31
|
+
path: '/x',
|
|
32
|
+
handler: 'x',
|
|
33
|
+
input: { body: { type: 'object', properties: { a: { type: 'string' } }, required: ['a'] } },
|
|
34
|
+
};
|
|
35
|
+
const def = buildRouteDefinition(ep, identitySchemaConverter);
|
|
36
|
+
expect((def.request as any).body.content['application/json'].schema).toEqual(ep.input!.body);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('convertRoutes imports a fetcher/fortress-authored route via toJSONSchemaConverter', () => {
|
|
40
|
+
const route = {
|
|
41
|
+
method: 'POST',
|
|
42
|
+
path: '/users',
|
|
43
|
+
responses: { 200: { description: 'ok' } },
|
|
44
|
+
request: { body: { content: { 'application/json': { schema: obj({ name: str() }, 'name') } } } },
|
|
45
|
+
};
|
|
46
|
+
const [ep] = convertRoutes([route as any], { schemaConverter: toJSONSchemaConverter });
|
|
47
|
+
expect(ep.input?.body?.type).toBe('object');
|
|
48
|
+
expect(ep.input?.body?.properties?.name?.type).toBe('string');
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default schema converters for the Hono OpenAPI integration — so you can mount
|
|
3
|
+
* fortress endpoints (or import external routes) **without pulling in Zod**.
|
|
4
|
+
*
|
|
5
|
+
* fortress's builder schemas (and `@bajustone/fetcher`'s builder schemas) ARE
|
|
6
|
+
* JSON Schema, so the converters are trivial: identity for the JSON-Schema
|
|
7
|
+
* direction, and `fromJSONSchema` when the target wants a validating Standard
|
|
8
|
+
* Schema V1. Existing Zod callers are unaffected — these are opt-in defaults.
|
|
9
|
+
*
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { JSONSchema } from '../core/json-schema';
|
|
14
|
+
import type { StandardSchemaV1 } from '../core/standard-schema';
|
|
15
|
+
import type { ToJSONSchemaConverter } from './convert-routes';
|
|
16
|
+
import type { SchemaConverter } from './openapi';
|
|
17
|
+
import { fromJSONSchema } from '@bajustone/fetcher/openapi';
|
|
18
|
+
import { extractJsonSchema } from '../core/schema-builder';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A {@link SchemaConverter} that passes JSON Schema through unchanged. Use with
|
|
22
|
+
* OpenAPI tooling that consumes JSON Schema directly, or for raw spec
|
|
23
|
+
* generation. Requires no schema library.
|
|
24
|
+
*/
|
|
25
|
+
export const identitySchemaConverter: SchemaConverter<JSONSchema> = jsonSchema => jsonSchema;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* A {@link SchemaConverter} that compiles JSON Schema into a validating
|
|
29
|
+
* `@bajustone/fetcher` Standard Schema V1. Use with Hono OpenAPI tooling that
|
|
30
|
+
* accepts Standard Schema (e.g. `hono-openapi` / `@hono/standard-validator`) —
|
|
31
|
+
* no Zod required.
|
|
32
|
+
*/
|
|
33
|
+
export const fetcherSchemaConverter: SchemaConverter<StandardSchemaV1> = jsonSchema =>
|
|
34
|
+
fromJSONSchema(jsonSchema) as unknown as StandardSchemaV1;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* A {@link ToJSONSchemaConverter} for importing external routes authored with
|
|
38
|
+
* fortress's or fetcher's builder (both ARE JSON Schema). Extracts clean JSON
|
|
39
|
+
* Schema (runtime-only `~`-prefixed props are dropped at OpenAPI emission).
|
|
40
|
+
* Pass to {@link convertRoutes} so such routes import without Zod.
|
|
41
|
+
*/
|
|
42
|
+
export const toJSONSchemaConverter: ToJSONSchemaConverter = schema =>
|
|
43
|
+
extractJsonSchema(schema as Parameters<typeof extractJsonSchema>[0]);
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Primary entry point for the Hono adapter: register a single fortress
|
|
3
|
+
* middleware that detects Fortress-managed paths and delegates to
|
|
4
|
+
* `fortress.handleRequest`. Custom user routes registered *before*
|
|
5
|
+
* `mountFortress` keep working — Hono's first-match routing means user
|
|
6
|
+
* handlers win over the catch-all.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { Hono } from 'hono';
|
|
11
|
+
* import { mountFortress } from '@bajustone/fortress/hono';
|
|
12
|
+
*
|
|
13
|
+
* const app = new Hono();
|
|
14
|
+
* mountFortress(app, fortress);
|
|
15
|
+
* // POST /auth/login, POST /auth/refresh, GET /auth/me, /iam/*, /oauth/* …
|
|
16
|
+
* // are all handled automatically. Cookies for login/refresh are set on
|
|
17
|
+
* // the response without any extra wiring.
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import type { Env, Hono } from 'hono';
|
|
22
|
+
import type { Fortress } from '../core/fortress';
|
|
23
|
+
import { buildRouteTable, matchRoute } from '../core/http/match';
|
|
24
|
+
|
|
25
|
+
/** Options for {@link mountFortress}. */
|
|
26
|
+
export interface MountFortressOptions {
|
|
27
|
+
/**
|
|
28
|
+
* Optional path prefix to mount Fortress under (e.g. `/api`). When set,
|
|
29
|
+
* a request to `/api/auth/login` is internally rewritten to `/auth/login`
|
|
30
|
+
* before being passed to `fortress.handleRequest`.
|
|
31
|
+
*/
|
|
32
|
+
prefix?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Mount a single Hono middleware that delegates Fortress-managed paths to
|
|
37
|
+
* `fortress.handleRequest` and otherwise calls `next()` so user-route
|
|
38
|
+
* handlers run normally.
|
|
39
|
+
*
|
|
40
|
+
* Cookies emitted by login/refresh/impersonate are honored automatically:
|
|
41
|
+
* the `Response` returned by `fortress.handleRequest` already carries the
|
|
42
|
+
* `Set-Cookie` headers built by core, and Hono returns it verbatim.
|
|
43
|
+
*/
|
|
44
|
+
export function mountFortress<E extends Env = Env>(
|
|
45
|
+
app: Hono<E>,
|
|
46
|
+
fortress: Fortress,
|
|
47
|
+
options: MountFortressOptions = {},
|
|
48
|
+
): void {
|
|
49
|
+
const prefix = options.prefix ?? '';
|
|
50
|
+
// Pre-build the route table once at startup. The middleware uses this to
|
|
51
|
+
// detect whether a request belongs to Fortress before delegating to core.
|
|
52
|
+
// Matching against the table catches every endpoint (auth, iam, plugin
|
|
53
|
+
// routes including OpenAPI's flat `/openapi.json` and `/openapi`) without
|
|
54
|
+
// needing per-prefix heuristics.
|
|
55
|
+
const routeTable = buildRouteTable(fortress.manifest.filter(route => route.mounted));
|
|
56
|
+
|
|
57
|
+
app.use('*', async (c, next) => {
|
|
58
|
+
const url = new URL(c.req.url);
|
|
59
|
+
let pathname = url.pathname;
|
|
60
|
+
if (prefix && pathname.startsWith(prefix)) {
|
|
61
|
+
pathname = pathname.slice(prefix.length) || '/';
|
|
62
|
+
}
|
|
63
|
+
else if (prefix) {
|
|
64
|
+
// Path doesn't live under our prefix — leave it for user routes.
|
|
65
|
+
await next();
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (!matchRoute(routeTable, c.req.method, pathname)) {
|
|
70
|
+
await next();
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Build a fresh Request with the (possibly rewritten) path so core
|
|
75
|
+
// matches the canonical /auth/* / /iam/* paths regardless of mount prefix.
|
|
76
|
+
const rewritten = new Request(`${url.origin}${pathname}${url.search}`, c.req.raw);
|
|
77
|
+
return fortress.handleRequest(rewritten);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration tests proving that api-key credentials authenticate user-owned
|
|
3
|
+
* Hono routes — the gap this branch closed. The auth middleware must delegate
|
|
4
|
+
* to `fortress.resolvePrincipal`, populate `fortressSubject` for every
|
|
5
|
+
* principal kind, and the RBAC middleware must check permissions by subject
|
|
6
|
+
* (not by hardcoded USER subject).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { Fortress } from '../core/fortress';
|
|
10
|
+
import type { Subject } from '../core/types';
|
|
11
|
+
import type { FortressEnv } from './middleware/auth';
|
|
12
|
+
import { Hono } from 'hono';
|
|
13
|
+
import { beforeEach, describe, expect, it } from 'vitest';
|
|
14
|
+
import { createFortress } from '../core/fortress';
|
|
15
|
+
import { apiKey } from '../plugins/api-key';
|
|
16
|
+
import { createTestAdapter } from '../testing';
|
|
17
|
+
import { getSubject, getUserId } from './helpers';
|
|
18
|
+
import { createHonoMiddleware } from './index';
|
|
19
|
+
|
|
20
|
+
const SECRET = 'hono-api-key-user-routes-secret-32char!';
|
|
21
|
+
|
|
22
|
+
interface Ctx {
|
|
23
|
+
fortress: Fortress<any>;
|
|
24
|
+
app: Hono<FortressEnv>;
|
|
25
|
+
userId: string;
|
|
26
|
+
userKey: string;
|
|
27
|
+
saId: string;
|
|
28
|
+
saKey: string;
|
|
29
|
+
saKeyNoPerm: string;
|
|
30
|
+
saNoPermId: string;
|
|
31
|
+
userAccessToken: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function setup(): Promise<Ctx> {
|
|
35
|
+
const fortress: Fortress<any> = createFortress({
|
|
36
|
+
jwt: { key: SECRET },
|
|
37
|
+
database: createTestAdapter(),
|
|
38
|
+
plugins: [apiKey({ prefix: 'test' })],
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Seed: a user, a service account with 'deploy:run' permission, and a
|
|
42
|
+
// second service account with *no* permissions (for the deny case).
|
|
43
|
+
const user = await fortress.auth.createUser({
|
|
44
|
+
email: 'human@example.com',
|
|
45
|
+
name: 'Human',
|
|
46
|
+
password: 'password-123456',
|
|
47
|
+
});
|
|
48
|
+
const userLogin = await fortress.auth.login('human@example.com', 'password-123456');
|
|
49
|
+
if (userLogin.status !== 'success')
|
|
50
|
+
throw new Error('login should succeed');
|
|
51
|
+
|
|
52
|
+
// Give the user the same 'deploy:run' permission so shared user routes work.
|
|
53
|
+
const deployRole = await fortress.iam.createRole('deployer', [
|
|
54
|
+
{ resource: 'deploy', action: 'run' },
|
|
55
|
+
]);
|
|
56
|
+
await fortress.iam.bindRoleToUser(user.id, deployRole.id);
|
|
57
|
+
|
|
58
|
+
const sa = await fortress.iam.createServiceAccount({ name: 'ci-deploy-bot' });
|
|
59
|
+
await fortress.iam.bindRoleToServiceAccount(sa.id, deployRole.id);
|
|
60
|
+
|
|
61
|
+
const saNoPerm = await fortress.iam.createServiceAccount({ name: 'observer-bot' });
|
|
62
|
+
|
|
63
|
+
const { key: userKey } = await fortress.plugins['api-key'].createKey({
|
|
64
|
+
subject: { type: 'USER', id: user.id },
|
|
65
|
+
name: 'user-key',
|
|
66
|
+
});
|
|
67
|
+
const { key: saKey } = await fortress.plugins['api-key'].createKey({
|
|
68
|
+
subject: { type: 'SERVICE_ACCOUNT', id: sa.id },
|
|
69
|
+
name: 'sa-key',
|
|
70
|
+
});
|
|
71
|
+
const { key: saKeyNoPerm } = await fortress.plugins['api-key'].createKey({
|
|
72
|
+
subject: { type: 'SERVICE_ACCOUNT', id: saNoPerm.id },
|
|
73
|
+
name: 'observer-key',
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const { authMiddleware, rbacMiddleware, errorHandler } = createHonoMiddleware(fortress, {
|
|
77
|
+
routeMap: {
|
|
78
|
+
'POST /deploy/run': { resource: 'deploy', action: 'run' },
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const app = new Hono<FortressEnv>();
|
|
83
|
+
app.onError(errorHandler);
|
|
84
|
+
app.use('/deploy/*', authMiddleware, rbacMiddleware);
|
|
85
|
+
app.use('/me/*', authMiddleware); // no rbac
|
|
86
|
+
|
|
87
|
+
app.post('/deploy/run', (c) => {
|
|
88
|
+
const subject = getSubject(c);
|
|
89
|
+
return c.json({ startedBy: subject });
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Endpoint that only USER subjects can reach (tests that getUserId throws
|
|
93
|
+
// for non-USER principals).
|
|
94
|
+
app.get('/me/profile', (c) => {
|
|
95
|
+
const uid = getUserId(c);
|
|
96
|
+
return c.json({ userId: uid });
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Endpoint that echoes the full subject so we can assert fortressSubject
|
|
100
|
+
// is populated and fortressUserId is USER-only.
|
|
101
|
+
app.get('/me/whoami', (c) => {
|
|
102
|
+
const subject = c.get('fortressSubject') as Subject;
|
|
103
|
+
const userId = c.get('fortressUserId');
|
|
104
|
+
return c.json({ subject, userId });
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
fortress,
|
|
109
|
+
app,
|
|
110
|
+
userId: user.id,
|
|
111
|
+
userKey,
|
|
112
|
+
saId: sa.id,
|
|
113
|
+
saKey,
|
|
114
|
+
saKeyNoPerm,
|
|
115
|
+
saNoPermId: saNoPerm.id,
|
|
116
|
+
userAccessToken: userLogin.accessToken,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
describe('hono user routes: api-key authentication', () => {
|
|
121
|
+
let ctx: Ctx;
|
|
122
|
+
beforeEach(async () => {
|
|
123
|
+
ctx = await setup();
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('authorization: ApiKey <key> authenticates a USER on a custom user route', async () => {
|
|
127
|
+
const res = await ctx.app.request('/me/whoami', {
|
|
128
|
+
headers: { Authorization: `ApiKey ${ctx.userKey}` },
|
|
129
|
+
});
|
|
130
|
+
expect(res.status).toBe(200);
|
|
131
|
+
const body = await res.json() as { subject: Subject; userId: string };
|
|
132
|
+
expect(body.subject).toEqual({ type: 'USER', id: ctx.userId });
|
|
133
|
+
expect(body.userId).toBe(ctx.userId);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('x-API-Key authenticates a SERVICE_ACCOUNT on a custom user route', async () => {
|
|
137
|
+
const res = await ctx.app.request('/me/whoami', {
|
|
138
|
+
headers: { 'X-API-Key': ctx.saKey },
|
|
139
|
+
});
|
|
140
|
+
expect(res.status).toBe(200);
|
|
141
|
+
const body = await res.json() as { subject: Subject; userId: string | null };
|
|
142
|
+
expect(body.subject).toEqual({ type: 'SERVICE_ACCOUNT', id: ctx.saId });
|
|
143
|
+
// fortressUserId is a USER-only alias — undefined for service accounts
|
|
144
|
+
expect(body.userId).toBeFalsy();
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('falls back to JWT bearer when no api-key header is present', async () => {
|
|
148
|
+
const res = await ctx.app.request('/me/whoami', {
|
|
149
|
+
headers: { Authorization: `Bearer ${ctx.userAccessToken}` },
|
|
150
|
+
});
|
|
151
|
+
expect(res.status).toBe(200);
|
|
152
|
+
const body = await res.json() as { subject: Subject; userId: string };
|
|
153
|
+
expect(body.subject).toEqual({ type: 'USER', id: ctx.userId });
|
|
154
|
+
expect(body.userId).toBe(ctx.userId);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('returns 401 when no credential is present', async () => {
|
|
158
|
+
const res = await ctx.app.request('/me/whoami');
|
|
159
|
+
expect(res.status).toBe(401);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('returns 401 for an unknown api-key', async () => {
|
|
163
|
+
const res = await ctx.app.request('/me/whoami', {
|
|
164
|
+
headers: { 'X-API-Key': 'test_sk_not-real' },
|
|
165
|
+
});
|
|
166
|
+
expect(res.status).toBe(401);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('getUserId throws 401 for a SERVICE_ACCOUNT principal (USER-only helper)', async () => {
|
|
170
|
+
const res = await ctx.app.request('/me/profile', {
|
|
171
|
+
headers: { 'X-API-Key': ctx.saKey },
|
|
172
|
+
});
|
|
173
|
+
expect(res.status).toBe(401);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('getUserId returns the id for a USER principal', async () => {
|
|
177
|
+
const res = await ctx.app.request('/me/profile', {
|
|
178
|
+
headers: { Authorization: `ApiKey ${ctx.userKey}` },
|
|
179
|
+
});
|
|
180
|
+
expect(res.status).toBe(200);
|
|
181
|
+
const body = await res.json() as { userId: string };
|
|
182
|
+
expect(body.userId).toBe(ctx.userId);
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
describe('hono user routes: api-key + RBAC middleware', () => {
|
|
187
|
+
let ctx: Ctx;
|
|
188
|
+
beforeEach(async () => {
|
|
189
|
+
ctx = await setup();
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('allows a SERVICE_ACCOUNT api-key when the bound role grants the permission', async () => {
|
|
193
|
+
const res = await ctx.app.request('/deploy/run', {
|
|
194
|
+
method: 'POST',
|
|
195
|
+
headers: { Authorization: `ApiKey ${ctx.saKey}` },
|
|
196
|
+
});
|
|
197
|
+
expect(res.status).toBe(200);
|
|
198
|
+
const body = await res.json() as { startedBy: Subject };
|
|
199
|
+
expect(body.startedBy).toEqual({ type: 'SERVICE_ACCOUNT', id: ctx.saId });
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('denies a SERVICE_ACCOUNT api-key that lacks the permission', async () => {
|
|
203
|
+
const res = await ctx.app.request('/deploy/run', {
|
|
204
|
+
method: 'POST',
|
|
205
|
+
headers: { 'X-API-Key': ctx.saKeyNoPerm },
|
|
206
|
+
});
|
|
207
|
+
expect(res.status).toBe(403);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('allows a USER api-key when the user has the permission', async () => {
|
|
211
|
+
const res = await ctx.app.request('/deploy/run', {
|
|
212
|
+
method: 'POST',
|
|
213
|
+
headers: { Authorization: `ApiKey ${ctx.userKey}` },
|
|
214
|
+
});
|
|
215
|
+
expect(res.status).toBe(200);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('allows the same USER over JWT bearer (control: JWT still works)', async () => {
|
|
219
|
+
const res = await ctx.app.request('/deploy/run', {
|
|
220
|
+
method: 'POST',
|
|
221
|
+
headers: { Authorization: `Bearer ${ctx.userAccessToken}` },
|
|
222
|
+
});
|
|
223
|
+
expect(res.status).toBe(200);
|
|
224
|
+
});
|
|
225
|
+
});
|