@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,493 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/plugins/audit-log/index.ts
|
|
21
|
+
var audit_log_exports = {};
|
|
22
|
+
__export(audit_log_exports, {
|
|
23
|
+
auditLog: () => auditLog
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(audit_log_exports);
|
|
26
|
+
async function sha256Hex(input) {
|
|
27
|
+
const data = new TextEncoder().encode(input);
|
|
28
|
+
const buffer = await crypto.subtle.digest("SHA-256", data);
|
|
29
|
+
return Array.from(new Uint8Array(buffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
30
|
+
}
|
|
31
|
+
var auditWriteChains = /* @__PURE__ */ new WeakMap();
|
|
32
|
+
function withAuditChainLock(db, fn) {
|
|
33
|
+
const run = () => db.transaction(async (tx) => {
|
|
34
|
+
if (tx.dialect === "pg") {
|
|
35
|
+
if (!tx.rawQuery)
|
|
36
|
+
throw new Error("PostgreSQL audit hash chains require rawQuery advisory-lock support");
|
|
37
|
+
await tx.rawQuery("SELECT pg_advisory_xact_lock(117993, 1)");
|
|
38
|
+
}
|
|
39
|
+
return fn(tx);
|
|
40
|
+
});
|
|
41
|
+
if (db.dialect === "pg")
|
|
42
|
+
return run();
|
|
43
|
+
const previous = auditWriteChains.get(db) ?? Promise.resolve();
|
|
44
|
+
const result = previous.then(run, run);
|
|
45
|
+
auditWriteChains.set(db, result.then(() => void 0, () => void 0));
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
var AUDIT_EXPORT_COLUMNS = [
|
|
49
|
+
"id",
|
|
50
|
+
"timestamp",
|
|
51
|
+
"eventType",
|
|
52
|
+
"actorId",
|
|
53
|
+
"actorType",
|
|
54
|
+
"targetId",
|
|
55
|
+
"targetType",
|
|
56
|
+
"ipAddress",
|
|
57
|
+
"userAgent",
|
|
58
|
+
"outcome",
|
|
59
|
+
"metadata",
|
|
60
|
+
"previousHash",
|
|
61
|
+
"createdAt"
|
|
62
|
+
];
|
|
63
|
+
var CSV_SPECIAL_RE = /[",\n\r]/;
|
|
64
|
+
var CSV_FORMULA_PREFIX_RE = /^[=+\-@\t\r]/;
|
|
65
|
+
function toCsvCell(value) {
|
|
66
|
+
if (value == null)
|
|
67
|
+
return "";
|
|
68
|
+
const raw = value instanceof Date ? value.toISOString() : String(value);
|
|
69
|
+
const str = CSV_FORMULA_PREFIX_RE.test(raw) ? `'${raw}` : raw;
|
|
70
|
+
if (CSV_SPECIAL_RE.test(str))
|
|
71
|
+
return `"${str.replace(/"/g, '""')}"`;
|
|
72
|
+
return str;
|
|
73
|
+
}
|
|
74
|
+
function entriesToCsv(entries) {
|
|
75
|
+
const header = AUDIT_EXPORT_COLUMNS.join(",");
|
|
76
|
+
const rows = entries.map((entry) => {
|
|
77
|
+
const record = entry;
|
|
78
|
+
return AUDIT_EXPORT_COLUMNS.map((column) => toCsvCell(record[column])).join(",");
|
|
79
|
+
});
|
|
80
|
+
return [header, ...rows].join("\n");
|
|
81
|
+
}
|
|
82
|
+
async function computeEntryHash(entry) {
|
|
83
|
+
const values = AUDIT_EXPORT_COLUMNS.map((column) => {
|
|
84
|
+
const value = entry[column];
|
|
85
|
+
return value instanceof Date ? value.toISOString() : value;
|
|
86
|
+
});
|
|
87
|
+
return sha256Hex(JSON.stringify(values));
|
|
88
|
+
}
|
|
89
|
+
async function inspectChain(entries, anchor) {
|
|
90
|
+
if (entries.length === 0) {
|
|
91
|
+
const brokenLinks2 = [];
|
|
92
|
+
if (!anchor) {
|
|
93
|
+
brokenLinks2.push({
|
|
94
|
+
entryId: "anchor",
|
|
95
|
+
expected: "persistent zero-entry audit-chain anchor",
|
|
96
|
+
actual: null
|
|
97
|
+
});
|
|
98
|
+
} else if (anchor.entryCount !== 0 || anchor.lastHash !== null) {
|
|
99
|
+
brokenLinks2.push({
|
|
100
|
+
entryId: "anchor",
|
|
101
|
+
expected: "entry_count=0 and last_hash=null",
|
|
102
|
+
actual: `${anchor.entryCount}:${anchor.lastHash ?? "null"}`
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
return { tail: null, brokenLinks: brokenLinks2 };
|
|
106
|
+
}
|
|
107
|
+
const hashes = /* @__PURE__ */ new Map();
|
|
108
|
+
const duplicateHashes = /* @__PURE__ */ new Set();
|
|
109
|
+
for (const entry of entries) {
|
|
110
|
+
const hash = await computeEntryHash(entry);
|
|
111
|
+
if (hashes.has(hash))
|
|
112
|
+
duplicateHashes.add(hash);
|
|
113
|
+
hashes.set(hash, entry);
|
|
114
|
+
}
|
|
115
|
+
const brokenLinks = [];
|
|
116
|
+
const roots = entries.filter((entry) => entry.previousHash == null);
|
|
117
|
+
for (const entry of entries) {
|
|
118
|
+
if (entry.previousHash != null && !hashes.has(entry.previousHash)) {
|
|
119
|
+
brokenLinks.push({
|
|
120
|
+
entryId: entry.id,
|
|
121
|
+
expected: "hash of an existing predecessor",
|
|
122
|
+
actual: entry.previousHash
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
for (const hash of duplicateHashes) {
|
|
127
|
+
brokenLinks.push({
|
|
128
|
+
entryId: hashes.get(hash).id,
|
|
129
|
+
expected: "unique entry hash",
|
|
130
|
+
actual: hash
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
if (roots.length !== 1) {
|
|
134
|
+
for (const root of roots.slice(1)) {
|
|
135
|
+
brokenLinks.push({
|
|
136
|
+
entryId: root.id,
|
|
137
|
+
expected: "exactly one chain root",
|
|
138
|
+
actual: null
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
if (roots.length === 0) {
|
|
142
|
+
brokenLinks.push({
|
|
143
|
+
entryId: entries[0].id,
|
|
144
|
+
expected: "one entry with previousHash=null",
|
|
145
|
+
actual: entries[0].previousHash
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const byPreviousHash = /* @__PURE__ */ new Map();
|
|
150
|
+
for (const entry of entries) {
|
|
151
|
+
if (entry.previousHash == null)
|
|
152
|
+
continue;
|
|
153
|
+
const successors = byPreviousHash.get(entry.previousHash) ?? [];
|
|
154
|
+
successors.push(entry);
|
|
155
|
+
byPreviousHash.set(entry.previousHash, successors);
|
|
156
|
+
}
|
|
157
|
+
const visited = /* @__PURE__ */ new Set();
|
|
158
|
+
let current = roots.length === 1 ? roots[0] : null;
|
|
159
|
+
let tail = null;
|
|
160
|
+
while (current && !visited.has(current)) {
|
|
161
|
+
visited.add(current);
|
|
162
|
+
tail = current;
|
|
163
|
+
const hash = await computeEntryHash(current);
|
|
164
|
+
const successors = byPreviousHash.get(hash) ?? [];
|
|
165
|
+
if (successors.length > 1) {
|
|
166
|
+
for (const successor of successors.slice(1)) {
|
|
167
|
+
brokenLinks.push({
|
|
168
|
+
entryId: successor.id,
|
|
169
|
+
expected: "a predecessor with only one successor",
|
|
170
|
+
actual: successor.previousHash
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
current = successors.length === 1 ? successors[0] : null;
|
|
175
|
+
}
|
|
176
|
+
for (const entry of entries) {
|
|
177
|
+
if (!visited.has(entry)) {
|
|
178
|
+
brokenLinks.push({
|
|
179
|
+
entryId: entry.id,
|
|
180
|
+
expected: "entry reachable from the chain root",
|
|
181
|
+
actual: entry.previousHash
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (!anchor) {
|
|
186
|
+
brokenLinks.push({
|
|
187
|
+
entryId: tail?.id ?? entries[0].id,
|
|
188
|
+
expected: "persisted terminal audit-chain anchor",
|
|
189
|
+
actual: null
|
|
190
|
+
});
|
|
191
|
+
} else {
|
|
192
|
+
if (anchor.entryCount !== entries.length) {
|
|
193
|
+
brokenLinks.push({
|
|
194
|
+
entryId: tail?.id ?? entries[0].id,
|
|
195
|
+
expected: `anchor entry count ${anchor.entryCount}`,
|
|
196
|
+
actual: String(entries.length)
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
if (tail) {
|
|
200
|
+
const tailHash = await computeEntryHash(tail);
|
|
201
|
+
if (anchor.lastHash !== tailHash) {
|
|
202
|
+
brokenLinks.push({
|
|
203
|
+
entryId: tail.id,
|
|
204
|
+
expected: anchor.lastHash ?? "non-null terminal hash",
|
|
205
|
+
actual: tailHash
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return { tail, brokenLinks };
|
|
211
|
+
}
|
|
212
|
+
async function queryAuditLog(db, options) {
|
|
213
|
+
const where = [];
|
|
214
|
+
if (options?.userId != null)
|
|
215
|
+
where.push({ field: "actorId", operator: "=", value: options.userId });
|
|
216
|
+
if (options?.eventType)
|
|
217
|
+
where.push({ field: "eventType", operator: "=", value: options.eventType });
|
|
218
|
+
if (options?.from)
|
|
219
|
+
where.push({ field: "timestamp", operator: ">=", value: options.from });
|
|
220
|
+
if (options?.to)
|
|
221
|
+
where.push({ field: "timestamp", operator: "<=", value: options.to });
|
|
222
|
+
return db.findMany({
|
|
223
|
+
model: "audit_log",
|
|
224
|
+
where: where.length > 0 ? where : void 0,
|
|
225
|
+
limit: options?.limit,
|
|
226
|
+
offset: options?.offset,
|
|
227
|
+
sortBy: { field: "timestamp", direction: "desc" }
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
function auditLog(config = {}) {
|
|
231
|
+
const allowedEvents = config.events ?? null;
|
|
232
|
+
const hashChain = config.hashChain ?? false;
|
|
233
|
+
function shouldLog(eventType) {
|
|
234
|
+
if (!allowedEvents)
|
|
235
|
+
return true;
|
|
236
|
+
return allowedEvents.includes(eventType);
|
|
237
|
+
}
|
|
238
|
+
async function readChainAnchor(db) {
|
|
239
|
+
return db.findOne({
|
|
240
|
+
model: "audit_chain_state",
|
|
241
|
+
where: [{ field: "id", operator: "=", value: "1" }]
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
async function writeEntry(db, entry) {
|
|
245
|
+
if (!hashChain) {
|
|
246
|
+
await db.create({ model: "audit_log", data: { ...entry, previousHash: null } });
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
await withAuditChainLock(db, async (tx) => {
|
|
250
|
+
const anchor = await readChainAnchor(tx);
|
|
251
|
+
if (!anchor || !Number.isInteger(anchor.entryCount) || anchor.entryCount < 0 || anchor.entryCount === 0 !== (anchor.lastHash === null)) {
|
|
252
|
+
throw new Error("Cannot append without a valid audit hash-chain anchor");
|
|
253
|
+
}
|
|
254
|
+
if (anchor.entryCount === 0) {
|
|
255
|
+
const existing = await tx.findMany({
|
|
256
|
+
model: "audit_log",
|
|
257
|
+
limit: 1
|
|
258
|
+
});
|
|
259
|
+
if (existing.length > 0) {
|
|
260
|
+
throw new Error(
|
|
261
|
+
"Cannot append to an unchained legacy audit log; call rebaselineChain() first"
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
const created = await tx.create({
|
|
266
|
+
model: "audit_log",
|
|
267
|
+
data: { ...entry, previousHash: anchor.lastHash }
|
|
268
|
+
});
|
|
269
|
+
const lastHash = await computeEntryHash(created);
|
|
270
|
+
const updated = await tx.update({
|
|
271
|
+
model: "audit_chain_state",
|
|
272
|
+
where: [{ field: "id", operator: "=", value: anchor.id }],
|
|
273
|
+
data: { lastHash, entryCount: anchor.entryCount + 1, updatedAt: /* @__PURE__ */ new Date() }
|
|
274
|
+
});
|
|
275
|
+
if (!updated)
|
|
276
|
+
throw new Error("Audit hash-chain anchor disappeared during append");
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
async function writeAuthEntry(db, entry, logger) {
|
|
280
|
+
try {
|
|
281
|
+
await writeEntry(db, entry);
|
|
282
|
+
} catch (error) {
|
|
283
|
+
try {
|
|
284
|
+
logger?.error({ plugin: "audit-log", error }, "audit log write failed");
|
|
285
|
+
} catch {
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return {
|
|
290
|
+
name: "audit-log",
|
|
291
|
+
models: [{
|
|
292
|
+
name: "audit_log",
|
|
293
|
+
fields: {
|
|
294
|
+
id: { type: "string", required: true },
|
|
295
|
+
timestamp: { type: "date", required: true },
|
|
296
|
+
eventType: { type: "string", required: true },
|
|
297
|
+
actorId: { type: "string" },
|
|
298
|
+
actorType: { type: "string", required: true },
|
|
299
|
+
targetId: { type: "string" },
|
|
300
|
+
targetType: { type: "string" },
|
|
301
|
+
ipAddress: { type: "string" },
|
|
302
|
+
userAgent: { type: "string" },
|
|
303
|
+
outcome: { type: "string", required: true },
|
|
304
|
+
metadata: { type: "string" },
|
|
305
|
+
previousHash: { type: "string" },
|
|
306
|
+
createdAt: { type: "date", required: true }
|
|
307
|
+
}
|
|
308
|
+
}, {
|
|
309
|
+
name: "audit_chain_state",
|
|
310
|
+
fields: {
|
|
311
|
+
id: { type: "string", required: true },
|
|
312
|
+
lastHash: { type: "string" },
|
|
313
|
+
entryCount: { type: "number", required: true },
|
|
314
|
+
updatedAt: { type: "date", required: true }
|
|
315
|
+
}
|
|
316
|
+
}],
|
|
317
|
+
hooks: {
|
|
318
|
+
async afterLogin(ctx, result) {
|
|
319
|
+
if (!shouldLog("LOGIN_SUCCESS"))
|
|
320
|
+
return result;
|
|
321
|
+
await writeAuthEntry(ctx.db, {
|
|
322
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
323
|
+
eventType: "LOGIN_SUCCESS",
|
|
324
|
+
actorId: result.user.id,
|
|
325
|
+
actorType: "user",
|
|
326
|
+
targetId: null,
|
|
327
|
+
targetType: null,
|
|
328
|
+
ipAddress: ctx.meta?.ipAddress ?? null,
|
|
329
|
+
userAgent: ctx.meta?.userAgent ?? null,
|
|
330
|
+
outcome: "success",
|
|
331
|
+
metadata: null
|
|
332
|
+
}, ctx.config.logger);
|
|
333
|
+
return result;
|
|
334
|
+
},
|
|
335
|
+
async onLoginFailure(ctx) {
|
|
336
|
+
if (!shouldLog("LOGIN_FAILURE"))
|
|
337
|
+
return;
|
|
338
|
+
await writeAuthEntry(ctx.db, {
|
|
339
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
340
|
+
eventType: "LOGIN_FAILURE",
|
|
341
|
+
actorId: null,
|
|
342
|
+
actorType: "anonymous",
|
|
343
|
+
targetId: null,
|
|
344
|
+
targetType: null,
|
|
345
|
+
ipAddress: null,
|
|
346
|
+
userAgent: null,
|
|
347
|
+
outcome: "failure",
|
|
348
|
+
metadata: JSON.stringify({ identifier: ctx.identifier, error: ctx.error.message })
|
|
349
|
+
}, ctx.config.logger);
|
|
350
|
+
},
|
|
351
|
+
async beforeLogout(ctx) {
|
|
352
|
+
if (!shouldLog("LOGOUT"))
|
|
353
|
+
return;
|
|
354
|
+
await writeAuthEntry(ctx.db, {
|
|
355
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
356
|
+
eventType: "LOGOUT",
|
|
357
|
+
actorId: null,
|
|
358
|
+
actorType: "user",
|
|
359
|
+
targetId: null,
|
|
360
|
+
targetType: null,
|
|
361
|
+
ipAddress: ctx.meta?.ipAddress ?? null,
|
|
362
|
+
userAgent: ctx.meta?.userAgent ?? null,
|
|
363
|
+
outcome: "success",
|
|
364
|
+
metadata: null
|
|
365
|
+
}, ctx.config.logger);
|
|
366
|
+
},
|
|
367
|
+
async afterRegister(ctx, user) {
|
|
368
|
+
if (!shouldLog("REGISTER"))
|
|
369
|
+
return;
|
|
370
|
+
await writeAuthEntry(ctx.db, {
|
|
371
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
372
|
+
eventType: "REGISTER",
|
|
373
|
+
actorId: user.id,
|
|
374
|
+
actorType: "user",
|
|
375
|
+
targetId: user.id,
|
|
376
|
+
targetType: "user",
|
|
377
|
+
ipAddress: ctx.meta?.ipAddress ?? null,
|
|
378
|
+
userAgent: ctx.meta?.userAgent ?? null,
|
|
379
|
+
outcome: "success",
|
|
380
|
+
metadata: null
|
|
381
|
+
}, ctx.config.logger);
|
|
382
|
+
},
|
|
383
|
+
async afterTokenRefresh(ctx, result) {
|
|
384
|
+
if (!shouldLog("TOKEN_REFRESH"))
|
|
385
|
+
return result;
|
|
386
|
+
await writeAuthEntry(ctx.db, {
|
|
387
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
388
|
+
eventType: "TOKEN_REFRESH",
|
|
389
|
+
actorId: null,
|
|
390
|
+
actorType: "user",
|
|
391
|
+
targetId: null,
|
|
392
|
+
targetType: null,
|
|
393
|
+
ipAddress: ctx.meta?.ipAddress ?? null,
|
|
394
|
+
userAgent: ctx.meta?.userAgent ?? null,
|
|
395
|
+
outcome: "success",
|
|
396
|
+
metadata: null
|
|
397
|
+
}, ctx.config.logger);
|
|
398
|
+
return result;
|
|
399
|
+
}
|
|
400
|
+
},
|
|
401
|
+
methods: (ctx) => ({
|
|
402
|
+
async getAuditLog(options) {
|
|
403
|
+
return queryAuditLog(ctx.db, options);
|
|
404
|
+
},
|
|
405
|
+
async logCustomEvent(event) {
|
|
406
|
+
const eventType = event.eventType;
|
|
407
|
+
if (allowedEvents && !allowedEvents.includes(eventType))
|
|
408
|
+
return;
|
|
409
|
+
await writeEntry(ctx.db, {
|
|
410
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
411
|
+
eventType,
|
|
412
|
+
actorId: event.actorId ?? null,
|
|
413
|
+
actorType: event.actorType ?? "system",
|
|
414
|
+
targetId: event.targetId ?? null,
|
|
415
|
+
targetType: event.targetType ?? null,
|
|
416
|
+
ipAddress: event.ipAddress ?? null,
|
|
417
|
+
userAgent: event.userAgent ?? null,
|
|
418
|
+
outcome: event.outcome ?? "success",
|
|
419
|
+
metadata: event.metadata ? JSON.stringify(event.metadata) : null
|
|
420
|
+
});
|
|
421
|
+
},
|
|
422
|
+
async rebaselineChain() {
|
|
423
|
+
if (!hashChain)
|
|
424
|
+
throw new Error("Enable hashChain before rebaselining the audit log");
|
|
425
|
+
return withAuditChainLock(ctx.db, async (tx) => {
|
|
426
|
+
const anchor = await readChainAnchor(tx);
|
|
427
|
+
if (!anchor)
|
|
428
|
+
throw new Error("Cannot rebaseline without the persistent audit hash-chain anchor");
|
|
429
|
+
if (anchor.entryCount !== 0 || anchor.lastHash !== null) {
|
|
430
|
+
throw new Error("Cannot rebaseline an audit hash chain that has already been initialized");
|
|
431
|
+
}
|
|
432
|
+
const entries = await tx.findMany({ model: "audit_log" });
|
|
433
|
+
if (entries.some((entry) => entry.previousHash !== null)) {
|
|
434
|
+
throw new Error("Cannot rebaseline a partially or previously chained audit log");
|
|
435
|
+
}
|
|
436
|
+
entries.sort((left, right) => {
|
|
437
|
+
const timeDifference = left.createdAt.getTime() - right.createdAt.getTime();
|
|
438
|
+
if (timeDifference !== 0)
|
|
439
|
+
return timeDifference;
|
|
440
|
+
return left.id < right.id ? -1 : left.id > right.id ? 1 : 0;
|
|
441
|
+
});
|
|
442
|
+
let previousHash = null;
|
|
443
|
+
const rebaselined = [];
|
|
444
|
+
for (const entry of entries) {
|
|
445
|
+
const updated = await tx.update({
|
|
446
|
+
model: "audit_log",
|
|
447
|
+
where: [{ field: "id", operator: "=", value: entry.id }],
|
|
448
|
+
data: { previousHash }
|
|
449
|
+
});
|
|
450
|
+
if (!updated)
|
|
451
|
+
throw new Error(`Audit entry ${entry.id} disappeared during rebaseline`);
|
|
452
|
+
rebaselined.push(updated);
|
|
453
|
+
previousHash = await computeEntryHash(updated);
|
|
454
|
+
}
|
|
455
|
+
const updatedAt = /* @__PURE__ */ new Date();
|
|
456
|
+
const updatedAnchor = await tx.update({
|
|
457
|
+
model: "audit_chain_state",
|
|
458
|
+
where: [{ field: "id", operator: "=", value: anchor.id }],
|
|
459
|
+
data: { lastHash: previousHash, entryCount: entries.length, updatedAt }
|
|
460
|
+
});
|
|
461
|
+
if (!updatedAnchor)
|
|
462
|
+
throw new Error("Audit hash-chain anchor disappeared during rebaseline");
|
|
463
|
+
const { brokenLinks } = await inspectChain(rebaselined, updatedAnchor);
|
|
464
|
+
if (brokenLinks.length > 0)
|
|
465
|
+
throw new Error("Rebaselined audit hash chain failed verification");
|
|
466
|
+
return { valid: true, totalEntries: entries.length, brokenLinks: [] };
|
|
467
|
+
});
|
|
468
|
+
},
|
|
469
|
+
async exportEntries(format = "json", options) {
|
|
470
|
+
const entries = await queryAuditLog(ctx.db, options);
|
|
471
|
+
if (format === "csv")
|
|
472
|
+
return entriesToCsv(entries);
|
|
473
|
+
return JSON.stringify(entries, null, 2);
|
|
474
|
+
},
|
|
475
|
+
async verifyChain() {
|
|
476
|
+
return withAuditChainLock(ctx.db, async (tx) => {
|
|
477
|
+
const entries = await tx.findMany({ model: "audit_log" });
|
|
478
|
+
const anchor = await readChainAnchor(tx);
|
|
479
|
+
const { brokenLinks } = await inspectChain(entries, anchor);
|
|
480
|
+
return {
|
|
481
|
+
valid: brokenLinks.length === 0,
|
|
482
|
+
totalEntries: entries.length,
|
|
483
|
+
brokenLinks
|
|
484
|
+
};
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
})
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
491
|
+
0 && (module.exports = {
|
|
492
|
+
auditLog
|
|
493
|
+
});
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { F as FortressPlugin } from '../config-GtlVt6ZD.cjs';
|
|
2
|
+
import '../index-CxEkfCA0.cjs';
|
|
3
|
+
import '../jwt.cjs';
|
|
4
|
+
import '../types-et0R8tsC.cjs';
|
|
5
|
+
import '../auth-service-BcyQ2PuZ.cjs';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Append-only audit log plugin for fortress.
|
|
9
|
+
*
|
|
10
|
+
* Captures auth and IAM lifecycle events into the `fortress_audit_log` table
|
|
11
|
+
* with a tamper-evident hash chain (each entry links to the previous entry's
|
|
12
|
+
* hash). Configurable event filtering and a query API for compliance reads.
|
|
13
|
+
*
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
interface AuditLogConfig {
|
|
18
|
+
/** Events to capture. Default: all. */
|
|
19
|
+
events?: AuditEventType[];
|
|
20
|
+
/** Enable hash chain for tamper detection. Default: false. */
|
|
21
|
+
hashChain?: boolean;
|
|
22
|
+
}
|
|
23
|
+
type AuditEventType = 'LOGIN_SUCCESS' | 'LOGIN_FAILURE' | 'LOGIN_PENDING' | 'MFA_VERIFY_SUCCESS' | 'MFA_VERIFY_FAILURE' | 'LOGOUT' | 'REGISTER' | 'TOKEN_REFRESH' | 'TOKEN_REUSE' | 'ROLE_CREATED' | 'ROLE_DELETED' | 'ROLE_UPDATED' | 'ROLE_BOUND' | 'ROLE_UNBOUND' | 'ROLE_PERMISSION_ADDED' | 'ROLE_PERMISSION_REMOVED' | 'PERMISSION_CHANGED' | 'PERMISSION_CREATED' | 'PERMISSION_DELETED' | 'GROUP_CREATED' | 'GROUP_UPDATED' | 'GROUP_DELETED' | 'GROUP_MEMBER_ADDED' | 'GROUP_MEMBER_REMOVED' | 'SERVICE_ACCOUNT_CREATED' | 'SERVICE_ACCOUNT_UPDATED' | 'SERVICE_ACCOUNT_DELETED';
|
|
24
|
+
interface AuditLogEntry {
|
|
25
|
+
id: string;
|
|
26
|
+
timestamp: Date;
|
|
27
|
+
eventType: AuditEventType;
|
|
28
|
+
actorId: string | null;
|
|
29
|
+
actorType: string;
|
|
30
|
+
targetId: string | null;
|
|
31
|
+
targetType: string | null;
|
|
32
|
+
ipAddress: string | null;
|
|
33
|
+
userAgent: string | null;
|
|
34
|
+
outcome: string;
|
|
35
|
+
metadata: string | null;
|
|
36
|
+
previousHash: string | null;
|
|
37
|
+
createdAt: Date;
|
|
38
|
+
}
|
|
39
|
+
interface AuditLogQueryOptions {
|
|
40
|
+
userId?: string;
|
|
41
|
+
eventType?: AuditEventType;
|
|
42
|
+
from?: Date;
|
|
43
|
+
to?: Date;
|
|
44
|
+
limit?: number;
|
|
45
|
+
offset?: number;
|
|
46
|
+
}
|
|
47
|
+
interface CustomAuditEvent {
|
|
48
|
+
eventType: string;
|
|
49
|
+
actorId?: string | null;
|
|
50
|
+
actorType?: string;
|
|
51
|
+
targetId?: string | null;
|
|
52
|
+
targetType?: string | null;
|
|
53
|
+
outcome?: string;
|
|
54
|
+
metadata?: Record<string, unknown> | null;
|
|
55
|
+
ipAddress?: string | null;
|
|
56
|
+
userAgent?: string | null;
|
|
57
|
+
}
|
|
58
|
+
interface ChainVerificationResult {
|
|
59
|
+
valid: boolean;
|
|
60
|
+
totalEntries: number;
|
|
61
|
+
brokenLinks: {
|
|
62
|
+
entryId: string;
|
|
63
|
+
expected: string;
|
|
64
|
+
actual: string | null;
|
|
65
|
+
}[];
|
|
66
|
+
}
|
|
67
|
+
/** Serialization formats supported by {@link AuditLogMethods.exportEntries}. */
|
|
68
|
+
type AuditLogExportFormat = 'json' | 'csv';
|
|
69
|
+
interface AuditLogMethods {
|
|
70
|
+
getAuditLog: (options?: AuditLogQueryOptions) => Promise<AuditLogEntry[]>;
|
|
71
|
+
logCustomEvent: (event: CustomAuditEvent) => Promise<void>;
|
|
72
|
+
verifyChain: () => Promise<ChainVerificationResult>;
|
|
73
|
+
/** Transactionally convert an unchained legacy log into a hash chain. */
|
|
74
|
+
rebaselineChain: () => Promise<ChainVerificationResult>;
|
|
75
|
+
exportEntries: (format?: AuditLogExportFormat, options?: AuditLogQueryOptions) => Promise<string>;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Audit log plugin factory. Returns a {@link FortressPlugin} that records
|
|
79
|
+
* auth and IAM lifecycle events into an append-only table with a
|
|
80
|
+
* tamper-evident hash chain, plus a query API for compliance reads.
|
|
81
|
+
*/
|
|
82
|
+
declare function auditLog(config?: AuditLogConfig): FortressPlugin & {
|
|
83
|
+
readonly name: 'audit-log';
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export { type AuditEventType, type AuditLogConfig, type AuditLogEntry, type AuditLogExportFormat, type AuditLogMethods, type AuditLogQueryOptions, type ChainVerificationResult, type CustomAuditEvent, auditLog };
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { F as FortressPlugin } from '../config-B2366s_G.js';
|
|
2
|
+
import '../index-CxEkfCA0.js';
|
|
3
|
+
import '../jwt.js';
|
|
4
|
+
import '../types-et0R8tsC.js';
|
|
5
|
+
import '../auth-service-BkzZkWfH.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Append-only audit log plugin for fortress.
|
|
9
|
+
*
|
|
10
|
+
* Captures auth and IAM lifecycle events into the `fortress_audit_log` table
|
|
11
|
+
* with a tamper-evident hash chain (each entry links to the previous entry's
|
|
12
|
+
* hash). Configurable event filtering and a query API for compliance reads.
|
|
13
|
+
*
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
interface AuditLogConfig {
|
|
18
|
+
/** Events to capture. Default: all. */
|
|
19
|
+
events?: AuditEventType[];
|
|
20
|
+
/** Enable hash chain for tamper detection. Default: false. */
|
|
21
|
+
hashChain?: boolean;
|
|
22
|
+
}
|
|
23
|
+
type AuditEventType = 'LOGIN_SUCCESS' | 'LOGIN_FAILURE' | 'LOGIN_PENDING' | 'MFA_VERIFY_SUCCESS' | 'MFA_VERIFY_FAILURE' | 'LOGOUT' | 'REGISTER' | 'TOKEN_REFRESH' | 'TOKEN_REUSE' | 'ROLE_CREATED' | 'ROLE_DELETED' | 'ROLE_UPDATED' | 'ROLE_BOUND' | 'ROLE_UNBOUND' | 'ROLE_PERMISSION_ADDED' | 'ROLE_PERMISSION_REMOVED' | 'PERMISSION_CHANGED' | 'PERMISSION_CREATED' | 'PERMISSION_DELETED' | 'GROUP_CREATED' | 'GROUP_UPDATED' | 'GROUP_DELETED' | 'GROUP_MEMBER_ADDED' | 'GROUP_MEMBER_REMOVED' | 'SERVICE_ACCOUNT_CREATED' | 'SERVICE_ACCOUNT_UPDATED' | 'SERVICE_ACCOUNT_DELETED';
|
|
24
|
+
interface AuditLogEntry {
|
|
25
|
+
id: string;
|
|
26
|
+
timestamp: Date;
|
|
27
|
+
eventType: AuditEventType;
|
|
28
|
+
actorId: string | null;
|
|
29
|
+
actorType: string;
|
|
30
|
+
targetId: string | null;
|
|
31
|
+
targetType: string | null;
|
|
32
|
+
ipAddress: string | null;
|
|
33
|
+
userAgent: string | null;
|
|
34
|
+
outcome: string;
|
|
35
|
+
metadata: string | null;
|
|
36
|
+
previousHash: string | null;
|
|
37
|
+
createdAt: Date;
|
|
38
|
+
}
|
|
39
|
+
interface AuditLogQueryOptions {
|
|
40
|
+
userId?: string;
|
|
41
|
+
eventType?: AuditEventType;
|
|
42
|
+
from?: Date;
|
|
43
|
+
to?: Date;
|
|
44
|
+
limit?: number;
|
|
45
|
+
offset?: number;
|
|
46
|
+
}
|
|
47
|
+
interface CustomAuditEvent {
|
|
48
|
+
eventType: string;
|
|
49
|
+
actorId?: string | null;
|
|
50
|
+
actorType?: string;
|
|
51
|
+
targetId?: string | null;
|
|
52
|
+
targetType?: string | null;
|
|
53
|
+
outcome?: string;
|
|
54
|
+
metadata?: Record<string, unknown> | null;
|
|
55
|
+
ipAddress?: string | null;
|
|
56
|
+
userAgent?: string | null;
|
|
57
|
+
}
|
|
58
|
+
interface ChainVerificationResult {
|
|
59
|
+
valid: boolean;
|
|
60
|
+
totalEntries: number;
|
|
61
|
+
brokenLinks: {
|
|
62
|
+
entryId: string;
|
|
63
|
+
expected: string;
|
|
64
|
+
actual: string | null;
|
|
65
|
+
}[];
|
|
66
|
+
}
|
|
67
|
+
/** Serialization formats supported by {@link AuditLogMethods.exportEntries}. */
|
|
68
|
+
type AuditLogExportFormat = 'json' | 'csv';
|
|
69
|
+
interface AuditLogMethods {
|
|
70
|
+
getAuditLog: (options?: AuditLogQueryOptions) => Promise<AuditLogEntry[]>;
|
|
71
|
+
logCustomEvent: (event: CustomAuditEvent) => Promise<void>;
|
|
72
|
+
verifyChain: () => Promise<ChainVerificationResult>;
|
|
73
|
+
/** Transactionally convert an unchained legacy log into a hash chain. */
|
|
74
|
+
rebaselineChain: () => Promise<ChainVerificationResult>;
|
|
75
|
+
exportEntries: (format?: AuditLogExportFormat, options?: AuditLogQueryOptions) => Promise<string>;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Audit log plugin factory. Returns a {@link FortressPlugin} that records
|
|
79
|
+
* auth and IAM lifecycle events into an append-only table with a
|
|
80
|
+
* tamper-evident hash chain, plus a query API for compliance reads.
|
|
81
|
+
*/
|
|
82
|
+
declare function auditLog(config?: AuditLogConfig): FortressPlugin & {
|
|
83
|
+
readonly name: 'audit-log';
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export { type AuditEventType, type AuditLogConfig, type AuditLogEntry, type AuditLogExportFormat, type AuditLogMethods, type AuditLogQueryOptions, type ChainVerificationResult, type CustomAuditEvent, auditLog };
|