@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,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Policy-as-code end-to-end tests (P1-7).
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { PolicyDocument } from './types';
|
|
6
|
+
import { describe, expect, it } from 'vitest';
|
|
7
|
+
import { createTestAdapter } from '../../testing';
|
|
8
|
+
import { createFortress } from '../fortress';
|
|
9
|
+
import { applyPolicyPlan } from './apply';
|
|
10
|
+
import { diffPolicy } from './diff';
|
|
11
|
+
|
|
12
|
+
const SECRET = 'policy-test-secret-at-least-32-bytes!';
|
|
13
|
+
|
|
14
|
+
function freshFortress() {
|
|
15
|
+
return createFortress({
|
|
16
|
+
jwt: { key: SECRET },
|
|
17
|
+
database: createTestAdapter(),
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const basePolicy: PolicyDocument = {
|
|
22
|
+
roles: [
|
|
23
|
+
{
|
|
24
|
+
name: 'editor',
|
|
25
|
+
description: 'Can edit articles',
|
|
26
|
+
permissions: [
|
|
27
|
+
{ resource: 'article', action: 'create' },
|
|
28
|
+
{ resource: 'article', action: 'update' },
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
groups: [
|
|
33
|
+
{ name: 'engineering', description: 'Engineering team' },
|
|
34
|
+
],
|
|
35
|
+
serviceAccounts: [
|
|
36
|
+
{ name: 'ci-bot', displayName: 'CI Bot', isActive: true, roles: ['editor'] },
|
|
37
|
+
],
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
describe('policy-as-code', () => {
|
|
41
|
+
it('diff against an empty IAM produces create ops for everything declared', async () => {
|
|
42
|
+
const fortress = freshFortress();
|
|
43
|
+
const plan = await diffPolicy(basePolicy, fortress.iam);
|
|
44
|
+
expect(plan.inSync).toBe(false);
|
|
45
|
+
const kinds = plan.ops.map(op => op.kind).sort();
|
|
46
|
+
expect(kinds).toContain('create-role');
|
|
47
|
+
expect(kinds).toContain('create-group');
|
|
48
|
+
expect(kinds).toContain('create-service-account');
|
|
49
|
+
expect(kinds).toContain('bind-service-account-role');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('apply runs every op and leaves the diff in-sync', async () => {
|
|
53
|
+
const fortress = freshFortress();
|
|
54
|
+
const plan = await diffPolicy(basePolicy, fortress.iam);
|
|
55
|
+
const result = await applyPolicyPlan(plan, fortress.iam);
|
|
56
|
+
expect(result.errors).toEqual([]);
|
|
57
|
+
expect(result.applied.length).toBeGreaterThan(0);
|
|
58
|
+
|
|
59
|
+
const second = await diffPolicy(basePolicy, fortress.iam);
|
|
60
|
+
expect(second.inSync).toBe(true);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('applies resource operations in memory and converges without filesystem access', async () => {
|
|
64
|
+
const fortress = freshFortress();
|
|
65
|
+
const policy: PolicyDocument = {
|
|
66
|
+
resources: [{ name: 'article', description: 'Articles', actions: ['read', 'write'] }],
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const result = await applyPolicyPlan(await diffPolicy(policy, fortress.iam), fortress.iam);
|
|
70
|
+
expect(result.errors).toEqual([]);
|
|
71
|
+
expect(result.applied.map(op => op.kind)).toEqual(['create-resource']);
|
|
72
|
+
expect((await fortress.iam.getResources()).resources.article).toMatchObject({
|
|
73
|
+
description: 'Articles',
|
|
74
|
+
actions: ['read', 'write'],
|
|
75
|
+
});
|
|
76
|
+
expect((await diffPolicy(policy, fortress.iam)).inSync).toBe(true);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('clears role descriptions and converges', async () => {
|
|
80
|
+
const fortress = freshFortress();
|
|
81
|
+
await applyPolicyPlan(await diffPolicy(basePolicy, fortress.iam), fortress.iam);
|
|
82
|
+
const cleared: PolicyDocument = {
|
|
83
|
+
...basePolicy,
|
|
84
|
+
roles: [{ ...basePolicy.roles![0], description: undefined }],
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const result = await applyPolicyPlan(await diffPolicy(cleared, fortress.iam), fortress.iam);
|
|
88
|
+
expect(result.errors).toEqual([]);
|
|
89
|
+
expect((await diffPolicy(cleared, fortress.iam)).inSync).toBe(true);
|
|
90
|
+
expect((await fortress.iam.getRoles()).find(role => role.name === 'editor')?.description).toBeNull();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('tracks real service-account bindings to zero-permission roles', async () => {
|
|
94
|
+
const fortress = freshFortress();
|
|
95
|
+
const policy: PolicyDocument = {
|
|
96
|
+
roles: [{ name: 'empty-role', permissions: [] }],
|
|
97
|
+
serviceAccounts: [{ name: 'empty-role-bot', roles: ['empty-role'] }],
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const result = await applyPolicyPlan(await diffPolicy(policy, fortress.iam), fortress.iam);
|
|
101
|
+
expect(result.errors).toEqual([]);
|
|
102
|
+
expect((await diffPolicy(policy, fortress.iam, { prune: true })).inSync).toBe(true);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('binds a newly declared role to an existing service account in one plan', async () => {
|
|
106
|
+
const fortress = freshFortress();
|
|
107
|
+
await fortress.iam.createServiceAccount({ name: 'existing-bot' });
|
|
108
|
+
const policy: PolicyDocument = {
|
|
109
|
+
roles: [{ name: 'new-role', permissions: [] }],
|
|
110
|
+
serviceAccounts: [{ name: 'existing-bot', roles: ['new-role'] }],
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const plan = await diffPolicy(policy, fortress.iam);
|
|
114
|
+
expect(plan.ops.map(op => op.kind)).toEqual(expect.arrayContaining([
|
|
115
|
+
'create-role',
|
|
116
|
+
'bind-service-account-role',
|
|
117
|
+
]));
|
|
118
|
+
const result = await applyPolicyPlan(plan, fortress.iam);
|
|
119
|
+
expect(result.errors).toEqual([]);
|
|
120
|
+
expect((await diffPolicy(policy, fortress.iam)).inSync).toBe(true);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('global policy unbinding preserves tenant-scoped role bindings', async () => {
|
|
124
|
+
const fortress = freshFortress();
|
|
125
|
+
const role = await fortress.iam.createRole('scoped-role', []);
|
|
126
|
+
const serviceAccount = await fortress.iam.createServiceAccount({ name: 'scoped-bot' });
|
|
127
|
+
await fortress.iam.bindRoleToServiceAccount(serviceAccount.id, role.id);
|
|
128
|
+
await fortress.iam.bindRoleToServiceAccount(serviceAccount.id, role.id, 'tenant-a');
|
|
129
|
+
const policy: PolicyDocument = {
|
|
130
|
+
roles: [{ name: 'scoped-role', permissions: [] }],
|
|
131
|
+
serviceAccounts: [{ name: 'scoped-bot', roles: [] }],
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const result = await applyPolicyPlan(
|
|
135
|
+
await diffPolicy(policy, fortress.iam, { prune: true }),
|
|
136
|
+
fortress.iam,
|
|
137
|
+
);
|
|
138
|
+
expect(result.errors).toEqual([]);
|
|
139
|
+
const remaining = await fortress.iam.listRoleBindingsForSubject({
|
|
140
|
+
type: 'SERVICE_ACCOUNT',
|
|
141
|
+
id: serviceAccount.id,
|
|
142
|
+
});
|
|
143
|
+
expect(remaining.map(binding => binding.tenantId)).toEqual(['tenant-a']);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('unbinds retained service accounts before pruning their old role', async () => {
|
|
147
|
+
const fortress = freshFortress();
|
|
148
|
+
const initial: PolicyDocument = {
|
|
149
|
+
roles: [{ name: 'old-role', permissions: [] }],
|
|
150
|
+
serviceAccounts: [{ name: 'retained-bot', roles: ['old-role'] }],
|
|
151
|
+
};
|
|
152
|
+
await applyPolicyPlan(await diffPolicy(initial, fortress.iam), fortress.iam);
|
|
153
|
+
const next: PolicyDocument = {
|
|
154
|
+
roles: [],
|
|
155
|
+
serviceAccounts: [{ name: 'retained-bot', roles: [] }],
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const plan = await diffPolicy(next, fortress.iam, { prune: true });
|
|
159
|
+
expect(plan.ops.map(op => op.kind)).toEqual(expect.arrayContaining([
|
|
160
|
+
'unbind-service-account-role',
|
|
161
|
+
'delete-role',
|
|
162
|
+
]));
|
|
163
|
+
const result = await applyPolicyPlan(plan, fortress.iam);
|
|
164
|
+
expect(result.errors).toEqual([]);
|
|
165
|
+
expect((await diffPolicy(next, fortress.iam, { prune: true })).inSync).toBe(true);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('add-role-permission op is emitted when the policy widens a role', async () => {
|
|
169
|
+
const fortress = freshFortress();
|
|
170
|
+
await applyPolicyPlan(await diffPolicy(basePolicy, fortress.iam), fortress.iam);
|
|
171
|
+
|
|
172
|
+
const widened: PolicyDocument = {
|
|
173
|
+
...basePolicy,
|
|
174
|
+
roles: [
|
|
175
|
+
{
|
|
176
|
+
...basePolicy.roles![0],
|
|
177
|
+
permissions: [
|
|
178
|
+
...basePolicy.roles![0].permissions,
|
|
179
|
+
{ resource: 'article', action: 'delete' },
|
|
180
|
+
],
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
};
|
|
184
|
+
const plan = await diffPolicy(widened, fortress.iam);
|
|
185
|
+
const ops = plan.ops.map(op => op.kind);
|
|
186
|
+
expect(ops).toContain('add-role-permission');
|
|
187
|
+
const result = await applyPolicyPlan(plan, fortress.iam);
|
|
188
|
+
expect(result.errors).toEqual([]);
|
|
189
|
+
expect((await diffPolicy(widened, fortress.iam)).inSync).toBe(true);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('remove-role-permission op is emitted when the policy narrows a role', async () => {
|
|
193
|
+
const fortress = freshFortress();
|
|
194
|
+
await applyPolicyPlan(await diffPolicy(basePolicy, fortress.iam), fortress.iam);
|
|
195
|
+
|
|
196
|
+
const narrowed: PolicyDocument = {
|
|
197
|
+
...basePolicy,
|
|
198
|
+
roles: [
|
|
199
|
+
{
|
|
200
|
+
...basePolicy.roles![0],
|
|
201
|
+
permissions: [{ resource: 'article', action: 'create' }],
|
|
202
|
+
},
|
|
203
|
+
],
|
|
204
|
+
};
|
|
205
|
+
const plan = await diffPolicy(narrowed, fortress.iam);
|
|
206
|
+
expect(plan.ops.some(op => op.kind === 'remove-role-permission')).toBe(true);
|
|
207
|
+
const result = await applyPolicyPlan(plan, fortress.iam);
|
|
208
|
+
expect(result.errors).toEqual([]);
|
|
209
|
+
expect((await diffPolicy(narrowed, fortress.iam)).inSync).toBe(true);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('requires explicit acknowledgement before pruning with an empty policy', async () => {
|
|
213
|
+
const fortress = freshFortress();
|
|
214
|
+
await applyPolicyPlan(await diffPolicy(basePolicy, fortress.iam), fortress.iam);
|
|
215
|
+
|
|
216
|
+
// Empty policy with prune should delete role/group/SA.
|
|
217
|
+
const empty: PolicyDocument = {};
|
|
218
|
+
await expect(diffPolicy(empty, fortress.iam, { prune: true })).rejects.toThrow('allowEmptyPrune');
|
|
219
|
+
const plan = await diffPolicy(empty, fortress.iam, { prune: true, allowEmptyPrune: true });
|
|
220
|
+
const kinds = plan.ops.map(op => op.kind).sort();
|
|
221
|
+
expect(kinds).toContain('delete-role');
|
|
222
|
+
expect(kinds).toContain('delete-group');
|
|
223
|
+
expect(kinds).toContain('delete-service-account');
|
|
224
|
+
|
|
225
|
+
const result = await applyPolicyPlan(plan, fortress.iam);
|
|
226
|
+
expect(result.errors).toEqual([]);
|
|
227
|
+
expect((await diffPolicy(empty, fortress.iam, {
|
|
228
|
+
prune: true,
|
|
229
|
+
allowEmptyPrune: true,
|
|
230
|
+
})).inSync).toBe(true);
|
|
231
|
+
});
|
|
232
|
+
});
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Declarative policy types for policy-as-code (P1-7).
|
|
3
|
+
*
|
|
4
|
+
* A policy file declares the desired state of Fortress-owned IAM
|
|
5
|
+
* resources: resources/actions, roles + permissions, groups, and
|
|
6
|
+
* service accounts (with their role bindings). The diff/apply pipeline
|
|
7
|
+
* computes the operations needed to reconcile the live database with the
|
|
8
|
+
* declared state.
|
|
9
|
+
*
|
|
10
|
+
* What's covered:
|
|
11
|
+
* - Resources + actions (extends today's `fortress.resources.json`).
|
|
12
|
+
* - Roles with permission lists.
|
|
13
|
+
* - Groups (declared shells; user memberships managed via runtime APIs).
|
|
14
|
+
* - Service accounts with role bindings.
|
|
15
|
+
*
|
|
16
|
+
* What's intentionally NOT covered:
|
|
17
|
+
* - OAuth clients (have secrets; manage via admin endpoints).
|
|
18
|
+
* - User accounts / user-group memberships (user data, not policy).
|
|
19
|
+
* - Tenant data and per-tenant role bindings (large scale; declare in
|
|
20
|
+
* per-tenant policy files if needed).
|
|
21
|
+
*
|
|
22
|
+
* @module
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/** Resource definition (action list shared with the existing `fortress.resources.json` shape). */
|
|
26
|
+
export interface PolicyResource {
|
|
27
|
+
name: string;
|
|
28
|
+
actions: string[];
|
|
29
|
+
description?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Permission entry inside a role declaration. `effect` defaults to ALLOW. */
|
|
33
|
+
export interface PolicyPermission {
|
|
34
|
+
resource: string;
|
|
35
|
+
action: string;
|
|
36
|
+
effect?: 'ALLOW' | 'DENY';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Role declaration. Permissions are the *complete* desired set for the role; extras are removed on apply. */
|
|
40
|
+
export interface PolicyRole {
|
|
41
|
+
name: string;
|
|
42
|
+
description?: string;
|
|
43
|
+
permissions: PolicyPermission[];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Group declaration. User memberships are intentionally not managed via policy files. */
|
|
47
|
+
export interface PolicyGroup {
|
|
48
|
+
name: string;
|
|
49
|
+
description?: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Service-account declaration. Roles list is the complete desired binding set; extras are unbound on apply. */
|
|
53
|
+
export interface PolicyServiceAccount {
|
|
54
|
+
name: string;
|
|
55
|
+
displayName?: string;
|
|
56
|
+
description?: string;
|
|
57
|
+
isActive?: boolean;
|
|
58
|
+
/** Role names to bind to this service account. Bindings outside this list are removed on apply. */
|
|
59
|
+
roles?: string[];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Top-level policy document. */
|
|
63
|
+
export interface PolicyDocument {
|
|
64
|
+
resources?: PolicyResource[];
|
|
65
|
+
roles?: PolicyRole[];
|
|
66
|
+
groups?: PolicyGroup[];
|
|
67
|
+
serviceAccounts?: PolicyServiceAccount[];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Single op produced by `diffPolicy`. Tag is human-readable; `description` flattens for logs. */
|
|
71
|
+
export type PolicyOp
|
|
72
|
+
= | { kind: 'create-resource'; resource: PolicyResource; description: string }
|
|
73
|
+
| { kind: 'add-resource-action'; resource: string; action: string; description: string }
|
|
74
|
+
| { kind: 'create-role'; role: PolicyRole; description: string }
|
|
75
|
+
| { kind: 'update-role-description'; role: string; from?: string; to?: string; description: string }
|
|
76
|
+
| { kind: 'add-role-permission'; role: string; permission: PolicyPermission; description: string }
|
|
77
|
+
| { kind: 'remove-role-permission'; role: string; permission: PolicyPermission; description: string }
|
|
78
|
+
| { kind: 'delete-role'; role: string; description: string }
|
|
79
|
+
| { kind: 'create-group'; group: PolicyGroup; description: string }
|
|
80
|
+
| { kind: 'update-group-description'; group: string; from?: string; to?: string; description: string }
|
|
81
|
+
| { kind: 'delete-group'; group: string; description: string }
|
|
82
|
+
| { kind: 'create-service-account'; serviceAccount: PolicyServiceAccount; description: string }
|
|
83
|
+
| { kind: 'update-service-account'; serviceAccount: string; changes: Record<string, unknown>; description: string }
|
|
84
|
+
| { kind: 'bind-service-account-role'; serviceAccount: string; role: string; description: string }
|
|
85
|
+
| { kind: 'unbind-service-account-role'; serviceAccount: string; role: string; description: string }
|
|
86
|
+
| { kind: 'delete-service-account'; serviceAccount: string; description: string };
|
|
87
|
+
|
|
88
|
+
/** Diff result returned by `diffPolicy`. */
|
|
89
|
+
export interface PolicyPlan {
|
|
90
|
+
ops: PolicyOp[];
|
|
91
|
+
/** `true` when there is nothing to reconcile. */
|
|
92
|
+
inSync: boolean;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Options for `diffPolicy`. */
|
|
96
|
+
export interface DiffPolicyOptions {
|
|
97
|
+
/**
|
|
98
|
+
* Prune resources/roles/groups/service-accounts present in the database
|
|
99
|
+
* but absent from the policy file. Default `false` — apply only adds
|
|
100
|
+
* and updates unless the operator opts in to deletions.
|
|
101
|
+
*/
|
|
102
|
+
prune?: boolean;
|
|
103
|
+
/** Explicitly acknowledge that pruning with no declared entities deletes all managed IAM state. */
|
|
104
|
+
allowEmptyPrune?: boolean;
|
|
105
|
+
}
|