@lamalibre/create-lamaste 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +118 -0
- package/README.md +114 -0
- package/bin/create-lamaste.js +15 -0
- package/package.json +54 -0
- package/scripts/bundle-vendor.js +110 -0
- package/scripts/lamaste-cert-info +62 -0
- package/scripts/lamaste-certbot +96 -0
- package/scripts/lamaste-priv +693 -0
- package/src/index.js +646 -0
- package/src/lib/accounts.js +86 -0
- package/src/lib/binaries.js +137 -0
- package/src/lib/cert-help-page.js +160 -0
- package/src/lib/env.js +132 -0
- package/src/lib/http-redirect.js +38 -0
- package/src/lib/ownership.js +231 -0
- package/src/lib/relay-services.js +103 -0
- package/src/lib/secrets.js +19 -0
- package/src/lib/service-config.js +311 -0
- package/src/lib/summary.js +131 -0
- package/src/lib/wrappers.js +63 -0
- package/src/tasks/gatekeeper.js +233 -0
- package/src/tasks/harden.js +293 -0
- package/src/tasks/mtls.js +193 -0
- package/src/tasks/nginx.js +265 -0
- package/src/tasks/node.js +110 -0
- package/src/tasks/panel.js +408 -0
- package/src/tasks/redeploy.js +438 -0
- package/vendor/docs/00-introduction/how-it-works.md +447 -0
- package/vendor/docs/00-introduction/quickstart.md +472 -0
- package/vendor/docs/00-introduction/what-is-lamaste.md +112 -0
- package/vendor/docs/01-concepts/authentication.md +464 -0
- package/vendor/docs/01-concepts/authorization.md +242 -0
- package/vendor/docs/01-concepts/certificates.md +452 -0
- package/vendor/docs/01-concepts/dns-and-domains.md +343 -0
- package/vendor/docs/01-concepts/mtls.md +490 -0
- package/vendor/docs/01-concepts/nginx-reverse-proxy.md +540 -0
- package/vendor/docs/01-concepts/security-model.md +600 -0
- package/vendor/docs/01-concepts/tickets.md +414 -0
- package/vendor/docs/01-concepts/tunneling.md +367 -0
- package/vendor/docs/02-guides/agent-setup.md +194 -0
- package/vendor/docs/02-guides/certificate-management.md +341 -0
- package/vendor/docs/02-guides/cloud-provisioning.md +406 -0
- package/vendor/docs/02-guides/desktop-app-setup.md +226 -0
- package/vendor/docs/02-guides/disaster-recovery.md +464 -0
- package/vendor/docs/02-guides/first-tunnel.md +230 -0
- package/vendor/docs/02-guides/installation.md +393 -0
- package/vendor/docs/02-guides/managing-users.md +255 -0
- package/vendor/docs/02-guides/onboarding.md +244 -0
- package/vendor/docs/02-guides/standalone-plugin-with-tickets.md +375 -0
- package/vendor/docs/02-guides/static-sites.md +364 -0
- package/vendor/docs/03-architecture/e2e-three-vm-sequences.md +457 -0
- package/vendor/docs/03-architecture/installer-flow.md +151 -0
- package/vendor/docs/03-architecture/installer.md +445 -0
- package/vendor/docs/03-architecture/management-flow.md +288 -0
- package/vendor/docs/03-architecture/nginx-configuration.md +830 -0
- package/vendor/docs/03-architecture/onboarding-flow.md +164 -0
- package/vendor/docs/03-architecture/overview.md +541 -0
- package/vendor/docs/03-architecture/panel-client.md +416 -0
- package/vendor/docs/03-architecture/panel-server.md +695 -0
- package/vendor/docs/03-architecture/state-management.md +529 -0
- package/vendor/docs/03-architecture/system-overview.md +167 -0
- package/vendor/docs/04-api-reference/certificates.md +1141 -0
- package/vendor/docs/04-api-reference/gatekeeper.md +592 -0
- package/vendor/docs/04-api-reference/onboarding.md +473 -0
- package/vendor/docs/04-api-reference/overview.md +404 -0
- package/vendor/docs/04-api-reference/services.md +267 -0
- package/vendor/docs/04-api-reference/settings.md +343 -0
- package/vendor/docs/04-api-reference/sites.md +716 -0
- package/vendor/docs/04-api-reference/system.md +268 -0
- package/vendor/docs/04-api-reference/tickets.md +618 -0
- package/vendor/docs/04-api-reference/tunnels.md +417 -0
- package/vendor/docs/04-api-reference/users.md +333 -0
- package/vendor/docs/05-operations/backup-and-restore.md +342 -0
- package/vendor/docs/05-operations/monitoring.md +285 -0
- package/vendor/docs/05-operations/uninstalling.md +344 -0
- package/vendor/docs/05-operations/upgrades.md +270 -0
- package/vendor/docs/06-reference/config-files.md +1080 -0
- package/vendor/docs/06-reference/glossary.md +528 -0
- package/vendor/docs/06-reference/installer-flags.md +260 -0
- package/vendor/docs/06-reference/ports-and-services.md +244 -0
- package/vendor/docs/06-reference/troubleshooting.md +665 -0
- package/vendor/docs/_index.json +233 -0
- package/vendor/docs/gatekeeper-design.md +953 -0
- package/vendor/docs/package.json +15 -0
- package/vendor/docs/todo/lib-cli-desktop-refactor.md +641 -0
- package/vendor/gatekeeper/dist/boot.d.ts +1 -0
- package/vendor/gatekeeper/dist/boot.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/boot.js +17 -0
- package/vendor/gatekeeper/dist/boot.js.map +1 -0
- package/vendor/gatekeeper/dist/cli/commands/access.d.ts +2 -0
- package/vendor/gatekeeper/dist/cli/commands/access.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/cli/commands/access.js +33 -0
- package/vendor/gatekeeper/dist/cli/commands/access.js.map +1 -0
- package/vendor/gatekeeper/dist/cli/commands/grants.d.ts +2 -0
- package/vendor/gatekeeper/dist/cli/commands/grants.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/cli/commands/grants.js +110 -0
- package/vendor/gatekeeper/dist/cli/commands/grants.js.map +1 -0
- package/vendor/gatekeeper/dist/cli/commands/groups.d.ts +2 -0
- package/vendor/gatekeeper/dist/cli/commands/groups.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/cli/commands/groups.js +98 -0
- package/vendor/gatekeeper/dist/cli/commands/groups.js.map +1 -0
- package/vendor/gatekeeper/dist/cli/index.d.ts +3 -0
- package/vendor/gatekeeper/dist/cli/index.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/cli/index.js +45 -0
- package/vendor/gatekeeper/dist/cli/index.js.map +1 -0
- package/vendor/gatekeeper/dist/index.d.ts +9 -0
- package/vendor/gatekeeper/dist/index.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/index.js +13 -0
- package/vendor/gatekeeper/dist/index.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/authz.d.ts +16 -0
- package/vendor/gatekeeper/dist/lib/authz.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/authz.js +42 -0
- package/vendor/gatekeeper/dist/lib/authz.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/cache-bust.d.ts +15 -0
- package/vendor/gatekeeper/dist/lib/cache-bust.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/cache-bust.js +19 -0
- package/vendor/gatekeeper/dist/lib/cache-bust.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/constants.d.ts +82 -0
- package/vendor/gatekeeper/dist/lib/constants.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/constants.js +79 -0
- package/vendor/gatekeeper/dist/lib/constants.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/grants.d.ts +32 -0
- package/vendor/gatekeeper/dist/lib/grants.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/grants.js +314 -0
- package/vendor/gatekeeper/dist/lib/grants.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/groups.d.ts +10 -0
- package/vendor/gatekeeper/dist/lib/groups.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/groups.js +247 -0
- package/vendor/gatekeeper/dist/lib/groups.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/migration.d.ts +17 -0
- package/vendor/gatekeeper/dist/lib/migration.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/migration.js +79 -0
- package/vendor/gatekeeper/dist/lib/migration.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/migrations/gatekeeper/0001_groups.sql +7 -0
- package/vendor/gatekeeper/dist/lib/migrations/gatekeeper/0002_access_grants.sql +14 -0
- package/vendor/gatekeeper/dist/lib/migrations/gatekeeper/0003_access_request_log.sql +9 -0
- package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0001_groups.sql +7 -0
- package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0002_access_grants.sql +14 -0
- package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0003_access_request_log.sql +9 -0
- package/vendor/gatekeeper/dist/lib/state-db.d.ts +16 -0
- package/vendor/gatekeeper/dist/lib/state-db.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/state-db.js +154 -0
- package/vendor/gatekeeper/dist/lib/state-db.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/templates.d.ts +21 -0
- package/vendor/gatekeeper/dist/lib/templates.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/templates.js +151 -0
- package/vendor/gatekeeper/dist/lib/templates.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/types.d.ts +178 -0
- package/vendor/gatekeeper/dist/lib/types.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/types.js +2 -0
- package/vendor/gatekeeper/dist/lib/types.js.map +1 -0
- package/vendor/gatekeeper/dist/server/index.d.ts +15 -0
- package/vendor/gatekeeper/dist/server/index.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/server/index.js +251 -0
- package/vendor/gatekeeper/dist/server/index.js.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/access-request.d.ts +7 -0
- package/vendor/gatekeeper/dist/server/routes/access-request.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/access-request.js +17 -0
- package/vendor/gatekeeper/dist/server/routes/access-request.js.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/authz.d.ts +3 -0
- package/vendor/gatekeeper/dist/server/routes/authz.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/authz.js +279 -0
- package/vendor/gatekeeper/dist/server/routes/authz.js.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/diagnostic.d.ts +13 -0
- package/vendor/gatekeeper/dist/server/routes/diagnostic.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/diagnostic.js +249 -0
- package/vendor/gatekeeper/dist/server/routes/diagnostic.js.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/grants.d.ts +3 -0
- package/vendor/gatekeeper/dist/server/routes/grants.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/grants.js +112 -0
- package/vendor/gatekeeper/dist/server/routes/grants.js.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/groups.d.ts +3 -0
- package/vendor/gatekeeper/dist/server/routes/groups.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/groups.js +139 -0
- package/vendor/gatekeeper/dist/server/routes/groups.js.map +1 -0
- package/vendor/gatekeeper/package.json +49 -0
- package/vendor/server/bin/lamaste-server.js +204 -0
- package/vendor/server/package.json +31 -0
- package/vendor/server/src/commands/certs.js +224 -0
- package/vendor/server/src/commands/chisel.js +132 -0
- package/vendor/server/src/commands/logs.js +49 -0
- package/vendor/server/src/commands/plugins.js +321 -0
- package/vendor/server/src/commands/reset-admin.js +478 -0
- package/vendor/server/src/commands/restart.js +94 -0
- package/vendor/server/src/commands/sites.js +257 -0
- package/vendor/server/src/commands/status.js +158 -0
- package/vendor/server/src/commands/tunnels.js +365 -0
- package/vendor/server/src/commands/uninstall.js +326 -0
- package/vendor/server/src/config.js +38 -0
- package/vendor/server/src/exec.js +26 -0
- package/vendor/server/src/logger.js +39 -0
- package/vendor/server/src/ndjson.js +56 -0
- package/vendor/server/src/panel-api.js +85 -0
- package/vendor/server/src/state.js +45 -0
- package/vendor/server-ui/dist/assets/index-B0nWuUyL.js +257 -0
- package/vendor/server-ui/dist/assets/index-BqWWFlTU.css +1 -0
- package/vendor/server-ui/dist/index.html +13 -0
- package/vendor/serverd/package.json +44 -0
- package/vendor/serverd/src/boot.js +13 -0
- package/vendor/serverd/src/index.js +191 -0
- package/vendor/serverd/src/lib/app-error.js +14 -0
- package/vendor/serverd/src/lib/authelia.js +85 -0
- package/vendor/serverd/src/lib/certbot.js +74 -0
- package/vendor/serverd/src/lib/chisel-args.js +6 -0
- package/vendor/serverd/src/lib/chisel-reconcile.js +232 -0
- package/vendor/serverd/src/lib/chisel-runtime.js +46 -0
- package/vendor/serverd/src/lib/chisel-users.js +109 -0
- package/vendor/serverd/src/lib/chisel.js +81 -0
- package/vendor/serverd/src/lib/config.js +190 -0
- package/vendor/serverd/src/lib/constants.js +8 -0
- package/vendor/serverd/src/lib/csr-signing.js +331 -0
- package/vendor/serverd/src/lib/enrollment.js +302 -0
- package/vendor/serverd/src/lib/files.js +19 -0
- package/vendor/serverd/src/lib/gatekeeper-client.js +135 -0
- package/vendor/serverd/src/lib/invite-page.js +281 -0
- package/vendor/serverd/src/lib/ip-utils.js +79 -0
- package/vendor/serverd/src/lib/migrations/state/0001_enrollment_tokens.sql +17 -0
- package/vendor/serverd/src/lib/migrations/state/0002_tickets.sql +70 -0
- package/vendor/serverd/src/lib/migrations/state/0003_plugins.sql +15 -0
- package/vendor/serverd/src/lib/migrations/state/0004_agents.sql +19 -0
- package/vendor/serverd/src/lib/mtls.js +795 -0
- package/vendor/serverd/src/lib/nginx.js +1090 -0
- package/vendor/serverd/src/lib/pki-paths.js +41 -0
- package/vendor/serverd/src/lib/pki-sign.js +103 -0
- package/vendor/serverd/src/lib/plugins.js +538 -0
- package/vendor/serverd/src/lib/push-install.js +390 -0
- package/vendor/serverd/src/lib/revocation.js +112 -0
- package/vendor/serverd/src/lib/services.js +131 -0
- package/vendor/serverd/src/lib/session.js +151 -0
- package/vendor/serverd/src/lib/state-db.js +130 -0
- package/vendor/serverd/src/lib/state.js +140 -0
- package/vendor/serverd/src/lib/storage.js +63 -0
- package/vendor/serverd/src/lib/system-stats.js +58 -0
- package/vendor/serverd/src/lib/tickets.js +1556 -0
- package/vendor/serverd/src/lib/totp.js +76 -0
- package/vendor/serverd/src/lib/tunnel-deps.js +41 -0
- package/vendor/serverd/src/lib/user-access-session.js +142 -0
- package/vendor/serverd/src/lib/user-access.js +52 -0
- package/vendor/serverd/src/middleware/errors.js +63 -0
- package/vendor/serverd/src/middleware/mtls.js +120 -0
- package/vendor/serverd/src/middleware/onboarding-guard.js +30 -0
- package/vendor/serverd/src/middleware/role-guard.js +58 -0
- package/vendor/serverd/src/middleware/twofa-session.js +65 -0
- package/vendor/serverd/src/middleware/user-access-session.js +52 -0
- package/vendor/serverd/src/routes/enrollment.js +120 -0
- package/vendor/serverd/src/routes/health.js +22 -0
- package/vendor/serverd/src/routes/invite.js +163 -0
- package/vendor/serverd/src/routes/management/agents.js +182 -0
- package/vendor/serverd/src/routes/management/certs.js +911 -0
- package/vendor/serverd/src/routes/management/gatekeeper-proxy.js +160 -0
- package/vendor/serverd/src/routes/management/identity.js +107 -0
- package/vendor/serverd/src/routes/management/invitations.js +166 -0
- package/vendor/serverd/src/routes/management/logs.js +218 -0
- package/vendor/serverd/src/routes/management/plugins.js +560 -0
- package/vendor/serverd/src/routes/management/services.js +63 -0
- package/vendor/serverd/src/routes/management/settings.js +325 -0
- package/vendor/serverd/src/routes/management/sites.js +522 -0
- package/vendor/serverd/src/routes/management/storage.js +192 -0
- package/vendor/serverd/src/routes/management/system.js +201 -0
- package/vendor/serverd/src/routes/management/tickets.js +457 -0
- package/vendor/serverd/src/routes/management/tunnels.js +709 -0
- package/vendor/serverd/src/routes/management/users.js +377 -0
- package/vendor/serverd/src/routes/management.js +36 -0
- package/vendor/serverd/src/routes/onboarding/dns.js +101 -0
- package/vendor/serverd/src/routes/onboarding/domain.js +42 -0
- package/vendor/serverd/src/routes/onboarding/index.js +18 -0
- package/vendor/serverd/src/routes/onboarding/provision.js +327 -0
- package/vendor/serverd/src/routes/onboarding/status.js +12 -0
- package/vendor/serverd/src/routes/plugin-router.js +79 -0
- package/vendor/serverd/src/routes/user-access.js +389 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { buildAccessRequestPage } from '../../lib/templates.js';
|
|
2
|
+
/**
|
|
3
|
+
* Standalone access-request page route.
|
|
4
|
+
* Used as a fallback when the inline 403 body approach isn't available.
|
|
5
|
+
*/
|
|
6
|
+
export async function accessRequestRoutes(fastify) {
|
|
7
|
+
fastify.get('/access-request', async (request, reply) => {
|
|
8
|
+
const tunnel = request.query.tunnel ?? 'unknown';
|
|
9
|
+
const username = request.headers['remote-user'] ?? 'unknown';
|
|
10
|
+
const html = buildAccessRequestPage(username, tunnel, {
|
|
11
|
+
adminContact: fastify.getSettings().adminEmail,
|
|
12
|
+
adminName: fastify.getSettings().adminName,
|
|
13
|
+
});
|
|
14
|
+
return reply.type('text/html; charset=utf-8').send(html);
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=access-request.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"access-request.js","sourceRoot":"","sources":["../../../src/server/routes/access-request.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAEhE;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,OAAwB;IAChE,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,KAAK,EAAE,OAAuB,EAAE,KAAmB,EAAE,EAAE;QACpF,MAAM,MAAM,GAAI,OAAO,CAAC,KAAgC,CAAC,MAAM,IAAI,SAAS,CAAC;QAC7E,MAAM,QAAQ,GAAI,OAAO,CAAC,OAAO,CAAC,aAAa,CAAY,IAAI,SAAS,CAAC;QAEzE,MAAM,IAAI,GAAG,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE;YACpD,YAAY,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC,UAAU;YAC9C,SAAS,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC,SAAS;SAC3C,CAAC,CAAC;QAEH,OAAO,KAAK,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authz.d.ts","sourceRoot":"","sources":["../../../src/server/routes/authz.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAgC,MAAM,SAAS,CAAC;AAkI7E,wBAAsB,WAAW,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CA4LzE"}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import { request as undiciRequest } from 'undici';
|
|
3
|
+
import { AUTHELIA_VERIFY_URL, SESSION_CACHE_TTL_MS } from '../../lib/constants.js';
|
|
4
|
+
import { checkAccess } from '../../lib/authz.js';
|
|
5
|
+
import { buildAccessRequestPage } from '../../lib/templates.js';
|
|
6
|
+
import { logAccessRequest } from '../routes/diagnostic.js';
|
|
7
|
+
/** Hard cap on session cache entries to prevent memory exhaustion on 512MB droplets. */
|
|
8
|
+
const MAX_SESSION_CACHE_ENTRIES = 10_000;
|
|
9
|
+
/** Cache-Control max-age for a successful authz response (matches nginx 200 TTL). */
|
|
10
|
+
const AUTHZ_SUCCESS_MAX_AGE_SECONDS = 10;
|
|
11
|
+
/** Upper bound on Authelia /verify call latency before we treat it as a denial. */
|
|
12
|
+
const AUTHELIA_VERIFY_TIMEOUT_MS = 2_500;
|
|
13
|
+
/**
|
|
14
|
+
* Per-process random key used to HMAC the cookie before storing it as a cache
|
|
15
|
+
* key. Without this, any actor with a valid cookie could compute the cache
|
|
16
|
+
* key and trigger collision-based attacks against cache-eviction logic.
|
|
17
|
+
*/
|
|
18
|
+
const COOKIE_HMAC_KEY = crypto.randomBytes(32);
|
|
19
|
+
/**
|
|
20
|
+
* Extract the Authelia session cookie value from the full cookie header.
|
|
21
|
+
*
|
|
22
|
+
* Returns null when the cookie header is missing the `authelia_session=`
|
|
23
|
+
* pair. The Authelia session cookie is the only auth signal we accept on
|
|
24
|
+
* this path — any other cookie (CSRF tokens, analytics, etc.) carries no
|
|
25
|
+
* identity. Falling back to the raw header would let unrelated cookies
|
|
26
|
+
* drive the cache key, which would either pollute the cache or — once a
|
|
27
|
+
* future refactor caches negative results — let an unauthenticated request
|
|
28
|
+
* poison a downstream entry.
|
|
29
|
+
*/
|
|
30
|
+
function extractAutheliaCookie(cookie) {
|
|
31
|
+
const match = cookie.match(/(?:^|;\s*)authelia_session=([^;]*)/);
|
|
32
|
+
return match?.[1] ?? null;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Derive a session cache key from a cookie string using HMAC-SHA256.
|
|
36
|
+
*
|
|
37
|
+
* The HMAC key is per-process random — keys are not predictable from cookie
|
|
38
|
+
* values alone, which prevents an outsider from probing/seeding the cache.
|
|
39
|
+
*/
|
|
40
|
+
function hashCookie(cookie) {
|
|
41
|
+
return crypto.createHmac('sha256', COOKIE_HMAC_KEY).update(cookie).digest('hex');
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Forward the request's cookies to Authelia's verify endpoint
|
|
45
|
+
* and extract the identity headers from the response.
|
|
46
|
+
*/
|
|
47
|
+
async function validateWithAuthelia(cookie, originalUrl, requestId) {
|
|
48
|
+
try {
|
|
49
|
+
// Authelia's `/api/authz/auth-request` needs both the target URL and
|
|
50
|
+
// method — without `X-Original-Method` it assumes `GET`, which is fine
|
|
51
|
+
// for navigation but silently mis-gates non-GET paths (e.g. form POSTs
|
|
52
|
+
// to protected endpoints). Send both.
|
|
53
|
+
const { statusCode, headers } = await undiciRequest(AUTHELIA_VERIFY_URL, {
|
|
54
|
+
method: 'GET',
|
|
55
|
+
headers: {
|
|
56
|
+
Cookie: cookie,
|
|
57
|
+
'X-Original-URL': originalUrl,
|
|
58
|
+
'X-Original-Method': 'GET',
|
|
59
|
+
'X-Forwarded-For': '127.0.0.1',
|
|
60
|
+
'X-Request-ID': requestId,
|
|
61
|
+
},
|
|
62
|
+
// Deny by default if Authelia is hung — the call gates every authz check
|
|
63
|
+
// on the hot path; a stuck request would block the whole request queue.
|
|
64
|
+
signal: AbortSignal.timeout(AUTHELIA_VERIFY_TIMEOUT_MS),
|
|
65
|
+
});
|
|
66
|
+
if (statusCode !== 200) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
const username = getHeader(headers, 'remote-user');
|
|
70
|
+
if (!username)
|
|
71
|
+
return null;
|
|
72
|
+
return {
|
|
73
|
+
username,
|
|
74
|
+
groups: getHeader(headers, 'remote-groups') ?? '',
|
|
75
|
+
displayName: getHeader(headers, 'remote-name') ?? '',
|
|
76
|
+
email: getHeader(headers, 'remote-email') ?? '',
|
|
77
|
+
expiresAt: Date.now() + SESSION_CACHE_TTL_MS,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function getHeader(headers, name) {
|
|
85
|
+
const val = headers[name];
|
|
86
|
+
if (Array.isArray(val))
|
|
87
|
+
return val[0];
|
|
88
|
+
return val;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Look up a tunnel by its FQDN from the in-memory cache.
|
|
92
|
+
*/
|
|
93
|
+
function findTunnelByFqdn(tunnels, fqdn) {
|
|
94
|
+
return tunnels.find((t) => t.fqdn === fqdn);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Parse an X-Original-URL header into its hostname and host components.
|
|
98
|
+
* Returns null when the value is not a parseable absolute URL.
|
|
99
|
+
*/
|
|
100
|
+
function parseOriginalUrl(originalUrl) {
|
|
101
|
+
try {
|
|
102
|
+
const parsed = new URL(originalUrl);
|
|
103
|
+
return { hostname: parsed.hostname, host: parsed.host };
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
// Route plugin
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
export async function authzRoutes(fastify) {
|
|
113
|
+
/**
|
|
114
|
+
* GET /authz/check — nginx auth_request target
|
|
115
|
+
*
|
|
116
|
+
* Flow:
|
|
117
|
+
* 1. Read Cookie and X-Original-URL from nginx
|
|
118
|
+
* 2. Validate Authelia session (cached in memory)
|
|
119
|
+
* 3. Look up tunnel by hostname
|
|
120
|
+
* 4. Check access mode and grants
|
|
121
|
+
* 5. Return 200 (allowed), 401 (not authenticated), or 403 (not authorized)
|
|
122
|
+
*
|
|
123
|
+
* On 403, the response body is the full access-request HTML page
|
|
124
|
+
* (nginx serves this inline on the tunnel's FQDN via error_page 403 =).
|
|
125
|
+
*/
|
|
126
|
+
fastify.get('/authz/check', {
|
|
127
|
+
// nginx is the only client (the gatekeeper binds 127.0.0.1) and
|
|
128
|
+
// it expects one call per cache miss on every protected request.
|
|
129
|
+
// Per-IP throttling here would punish legitimate traffic because
|
|
130
|
+
// every caller arrives as 127.0.0.1. We collapse the key to a
|
|
131
|
+
// single bucket and raise the ceiling to an effectively-unlimited
|
|
132
|
+
// 10k/min so the rate-limit is structurally present (CodeQL
|
|
133
|
+
// js/missing-rate-limiting) without breaking the hot path.
|
|
134
|
+
config: {
|
|
135
|
+
rateLimit: {
|
|
136
|
+
max: 10_000,
|
|
137
|
+
timeWindow: '1 minute',
|
|
138
|
+
keyGenerator: () => 'gatekeeper-internal',
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
}, async (request, reply) => {
|
|
142
|
+
const requestId = request.id;
|
|
143
|
+
const cookie = request.headers['cookie'] ?? '';
|
|
144
|
+
const originalUrl = request.headers['x-original-url'] ?? '';
|
|
145
|
+
if (!cookie || !originalUrl) {
|
|
146
|
+
return reply.code(401).send();
|
|
147
|
+
}
|
|
148
|
+
// Parse and validate X-Original-URL before any other work.
|
|
149
|
+
// Defense-in-depth: the 127.0.0.1 bind + nginx auth_request boundary is
|
|
150
|
+
// the primary guarantee that this header is trustworthy; host matching
|
|
151
|
+
// ensures a misconfigured proxy chain cannot forge tunnel identity.
|
|
152
|
+
const parsed = parseOriginalUrl(originalUrl);
|
|
153
|
+
if (!parsed) {
|
|
154
|
+
request.log.info({ requestId, reason: 'invalid_original_url' }, 'authz denied');
|
|
155
|
+
return reply.code(401).send();
|
|
156
|
+
}
|
|
157
|
+
// Host header check: when nginx forwards the parent request's Host to the
|
|
158
|
+
// subrequest (default behavior for auth_request), the hostname portion
|
|
159
|
+
// must match the hostname parsed from X-Original-URL. Port differences
|
|
160
|
+
// are ignored — nginx often normalizes them. An absent Host header skips
|
|
161
|
+
// the check (some proxies strip it from internal subrequests).
|
|
162
|
+
const hostHeader = request.headers['host'] ?? '';
|
|
163
|
+
if (hostHeader) {
|
|
164
|
+
const hostOnly = hostHeader.split(':')[0]?.toLowerCase() ?? '';
|
|
165
|
+
if (hostOnly && hostOnly !== parsed.hostname.toLowerCase()) {
|
|
166
|
+
request.log.info({
|
|
167
|
+
requestId,
|
|
168
|
+
reason: 'host_header_mismatch',
|
|
169
|
+
originalHost: parsed.hostname,
|
|
170
|
+
hostHeader,
|
|
171
|
+
}, 'authz denied');
|
|
172
|
+
return reply.code(401).send();
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
const tunnels = fastify.getTunnels();
|
|
176
|
+
const tunnel = findTunnelByFqdn(tunnels, parsed.hostname);
|
|
177
|
+
if (!tunnel) {
|
|
178
|
+
request.log.info({ requestId, reason: 'unknown_tunnel_fqdn', fqdn: parsed.hostname }, 'authz denied');
|
|
179
|
+
return reply.code(401).send();
|
|
180
|
+
}
|
|
181
|
+
// Check session cache (keyed by Authelia session cookie only to prevent
|
|
182
|
+
// cache pollution via unrelated cookies like CSRF tokens or analytics).
|
|
183
|
+
const autheliaCookie = extractAutheliaCookie(cookie);
|
|
184
|
+
if (!autheliaCookie) {
|
|
185
|
+
// No authelia_session — short-circuit before any cache work or the
|
|
186
|
+
// upstream Authelia /verify call. Without this, an unauthenticated
|
|
187
|
+
// request would still cost us an Authelia round-trip.
|
|
188
|
+
request.log.info({ requestId, reason: 'missing_authelia_cookie', fqdn: parsed.hostname }, 'authz denied');
|
|
189
|
+
return reply.code(401).send();
|
|
190
|
+
}
|
|
191
|
+
const cacheKey = hashCookie(autheliaCookie);
|
|
192
|
+
const sessionCache = fastify.getSessionCache();
|
|
193
|
+
const cached = sessionCache.get(cacheKey);
|
|
194
|
+
const now = Date.now();
|
|
195
|
+
let session;
|
|
196
|
+
if (cached && cached.expiresAt > now) {
|
|
197
|
+
session = cached;
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
if (cached) {
|
|
201
|
+
// Lazy TTL eviction — one stale entry pruned per request.
|
|
202
|
+
sessionCache.delete(cacheKey);
|
|
203
|
+
}
|
|
204
|
+
const validated = await validateWithAuthelia(cookie, originalUrl, requestId);
|
|
205
|
+
if (!validated) {
|
|
206
|
+
request.log.info({ requestId, reason: 'authelia_rejected', fqdn: parsed.hostname }, 'authz denied');
|
|
207
|
+
return reply.code(401).send();
|
|
208
|
+
}
|
|
209
|
+
// Opportunistic eviction — drop oldest (insertion-order head) when at cap.
|
|
210
|
+
while (sessionCache.size >= MAX_SESSION_CACHE_ENTRIES) {
|
|
211
|
+
const oldestKey = sessionCache.keys().next().value;
|
|
212
|
+
if (oldestKey === undefined)
|
|
213
|
+
break;
|
|
214
|
+
sessionCache.delete(oldestKey);
|
|
215
|
+
}
|
|
216
|
+
sessionCache.set(cacheKey, validated);
|
|
217
|
+
session = validated;
|
|
218
|
+
}
|
|
219
|
+
// Check access mode
|
|
220
|
+
if (tunnel.accessMode === 'public' || tunnel.accessMode === 'authenticated') {
|
|
221
|
+
setAutheliaHeaders(reply, session);
|
|
222
|
+
reply.header('Cache-Control', `public, max-age=${AUTHZ_SUCCESS_MAX_AGE_SECONDS}`);
|
|
223
|
+
reply.header('X-Gatekeeper-Cache-Version', String(fastify.getCacheVersion()));
|
|
224
|
+
return reply.code(200).send();
|
|
225
|
+
}
|
|
226
|
+
// restricted — check grants
|
|
227
|
+
const result = await checkAccess(session.username, 'tunnel', tunnel.id, {
|
|
228
|
+
adminContact: fastify.getSettings().adminEmail,
|
|
229
|
+
adminName: fastify.getSettings().adminName,
|
|
230
|
+
});
|
|
231
|
+
if (result.allowed) {
|
|
232
|
+
setAutheliaHeaders(reply, session);
|
|
233
|
+
reply.header('Cache-Control', `public, max-age=${AUTHZ_SUCCESS_MAX_AGE_SECONDS}`);
|
|
234
|
+
reply.header('X-Gatekeeper-Cache-Version', String(fastify.getCacheVersion()));
|
|
235
|
+
return reply.code(200).send();
|
|
236
|
+
}
|
|
237
|
+
request.log.info({
|
|
238
|
+
requestId,
|
|
239
|
+
reason: 'no_matching_grant',
|
|
240
|
+
username: session.username,
|
|
241
|
+
resourceType: 'tunnel',
|
|
242
|
+
resourceId: tunnel.id,
|
|
243
|
+
requiredGrant: `tunnel:${tunnel.id}`,
|
|
244
|
+
fqdn: parsed.hostname,
|
|
245
|
+
}, 'authz denied');
|
|
246
|
+
if (fastify.getSettings().accessLoggingEnabled) {
|
|
247
|
+
logAccessRequest({
|
|
248
|
+
timestamp: new Date().toISOString(),
|
|
249
|
+
username: session.username,
|
|
250
|
+
resourceType: 'tunnel',
|
|
251
|
+
resourceId: tunnel.id,
|
|
252
|
+
resourceFqdn: parsed.hostname,
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
// Access denied — return inline HTML page as 403 body.
|
|
256
|
+
// Cache-Control: no-store so nginx never caches the denial; the matching
|
|
257
|
+
// proxy_cache_valid 403 0 in the snippet is the authoritative setting.
|
|
258
|
+
const html = buildAccessRequestPage(session.username, parsed.hostname, {
|
|
259
|
+
adminContact: fastify.getSettings().adminEmail,
|
|
260
|
+
adminName: fastify.getSettings().adminName,
|
|
261
|
+
});
|
|
262
|
+
return reply
|
|
263
|
+
.code(403)
|
|
264
|
+
.header('Cache-Control', 'no-store')
|
|
265
|
+
.type('text/html; charset=utf-8')
|
|
266
|
+
.send(html);
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Set Authelia identity headers on the response so nginx can
|
|
271
|
+
* forward them to the tunnel backend.
|
|
272
|
+
*/
|
|
273
|
+
function setAutheliaHeaders(reply, session) {
|
|
274
|
+
reply.header('remote-user', session.username);
|
|
275
|
+
reply.header('remote-groups', session.groups);
|
|
276
|
+
reply.header('remote-name', session.displayName);
|
|
277
|
+
reply.header('remote-email', session.email);
|
|
278
|
+
}
|
|
279
|
+
//# sourceMappingURL=authz.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authz.js","sourceRoot":"","sources":["../../../src/server/routes/authz.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { FastifyInstance } from 'fastify';
|
|
2
|
+
import type { AccessRequestEntry } from '../../lib/types.js';
|
|
3
|
+
export declare function diagnosticRoutes(fastify: FastifyInstance): Promise<void>;
|
|
4
|
+
/**
|
|
5
|
+
* Log a denied access request (called from authz route).
|
|
6
|
+
*
|
|
7
|
+
* Non-blocking: enqueues the entry and returns synchronously. A background
|
|
8
|
+
* drainer flushes batches every 100ms via a single SQLite transaction.
|
|
9
|
+
* Entries queued within the last 100ms of a crash may be lost — the trade-off
|
|
10
|
+
* for keeping the nginx hot path free of disk I/O.
|
|
11
|
+
*/
|
|
12
|
+
export declare function logAccessRequest(entry: AccessRequestEntry): void;
|
|
13
|
+
//# sourceMappingURL=diagnostic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnostic.d.ts","sourceRoot":"","sources":["../../../src/server/routes/diagnostic.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAgC,MAAM,SAAS,CAAC;AAO7E,OAAO,KAAK,EAAsB,kBAAkB,EAAoB,MAAM,oBAAoB,CAAC;AAyLnG,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAgG9E;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,kBAAkB,GAAG,IAAI,CAEhE"}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { atomicWriteJSON } from '@lamalibre/lamaste';
|
|
4
|
+
import { checkAccess } from '../../lib/authz.js';
|
|
5
|
+
import { DEFAULT_DATA_DIR, SETTINGS_FILE } from '../../lib/constants.js';
|
|
6
|
+
import { getGatekeeperDb } from '../../lib/state-db.js';
|
|
7
|
+
const dataDir = process.env['LAMALIBRE_LAMASTE_DATA_DIR'] ?? DEFAULT_DATA_DIR;
|
|
8
|
+
const settingsPath = path.join(dataDir, SETTINGS_FILE);
|
|
9
|
+
const LOG_QUEUE_MAX = 1000;
|
|
10
|
+
const LOG_DRAIN_INTERVAL_MS = 100;
|
|
11
|
+
const LOG_OVERFLOW_WARN_INTERVAL_MS = 60_000;
|
|
12
|
+
const DEFAULT_RETENTION_DAYS = 90;
|
|
13
|
+
// Retention sweep cadence — once per drain tick is wasteful (DELETE every
|
|
14
|
+
// 100ms), and once per process lifetime is too coarse. Hourly matches the
|
|
15
|
+
// JSON-era rotation cadence loosely and is comfortably below any sensible
|
|
16
|
+
// retention setting.
|
|
17
|
+
const RETENTION_SWEEP_INTERVAL_MS = 60 * 60 * 1000;
|
|
18
|
+
const UpdateSettingsSchema = z.object({
|
|
19
|
+
adminEmail: z.string().email().max(200).optional(),
|
|
20
|
+
adminName: z.string().max(200).optional(),
|
|
21
|
+
slackChannel: z.string().max(200).optional(),
|
|
22
|
+
teamsChannel: z.string().max(200).optional(),
|
|
23
|
+
sessionCacheTtlMs: z.number().int().min(1000).max(300000).optional(),
|
|
24
|
+
accessLoggingEnabled: z.boolean().optional(),
|
|
25
|
+
accessLogRetentionDays: z.number().int().min(1).max(365).optional(),
|
|
26
|
+
});
|
|
27
|
+
let stmts = null;
|
|
28
|
+
async function getStmts() {
|
|
29
|
+
if (stmts)
|
|
30
|
+
return stmts;
|
|
31
|
+
const db = await getGatekeeperDb();
|
|
32
|
+
stmts = {
|
|
33
|
+
insert: db.prepare(`
|
|
34
|
+
INSERT INTO access_request_log
|
|
35
|
+
(timestamp, username, resource_type, resource_id, resource_fqdn)
|
|
36
|
+
VALUES (?, ?, ?, ?, ?)
|
|
37
|
+
`),
|
|
38
|
+
// Newest-first pagination: id is autoincrement so DESC order matches
|
|
39
|
+
// insertion order more cheaply than sorting on `timestamp`.
|
|
40
|
+
selectPage: db.prepare('SELECT * FROM access_request_log ORDER BY id DESC LIMIT ? OFFSET ?'),
|
|
41
|
+
countAll: db.prepare('SELECT COUNT(*) AS n FROM access_request_log'),
|
|
42
|
+
deleteAll: db.prepare('DELETE FROM access_request_log'),
|
|
43
|
+
deleteOlderThan: db.prepare('DELETE FROM access_request_log WHERE timestamp < ?'),
|
|
44
|
+
begin: db.prepare('BEGIN IMMEDIATE'),
|
|
45
|
+
commit: db.prepare('COMMIT'),
|
|
46
|
+
rollback: db.prepare('ROLLBACK'),
|
|
47
|
+
};
|
|
48
|
+
return stmts;
|
|
49
|
+
}
|
|
50
|
+
function rowToEntry(row) {
|
|
51
|
+
return {
|
|
52
|
+
timestamp: row.timestamp,
|
|
53
|
+
username: row.username,
|
|
54
|
+
resourceType: row.resource_type,
|
|
55
|
+
resourceId: row.resource_id,
|
|
56
|
+
resourceFqdn: row.resource_fqdn,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
// Persistence (batched insert, retention sweep)
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
async function persistEntries(entries) {
|
|
63
|
+
if (entries.length === 0)
|
|
64
|
+
return;
|
|
65
|
+
const s = await getStmts();
|
|
66
|
+
s.begin.run();
|
|
67
|
+
try {
|
|
68
|
+
for (const e of entries) {
|
|
69
|
+
s.insert.run(e.timestamp, e.username, e.resourceType, e.resourceId, e.resourceFqdn);
|
|
70
|
+
}
|
|
71
|
+
s.commit.run();
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
s.rollback.run();
|
|
75
|
+
throw err;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
let lastRetentionSweepAt = 0;
|
|
79
|
+
let retentionDaysOverride = null;
|
|
80
|
+
function setRetentionDays(days) {
|
|
81
|
+
retentionDaysOverride = days;
|
|
82
|
+
}
|
|
83
|
+
async function maybeSweepRetention() {
|
|
84
|
+
const now = Date.now();
|
|
85
|
+
if (now - lastRetentionSweepAt < RETENTION_SWEEP_INTERVAL_MS)
|
|
86
|
+
return;
|
|
87
|
+
lastRetentionSweepAt = now;
|
|
88
|
+
const days = retentionDaysOverride ?? DEFAULT_RETENTION_DAYS;
|
|
89
|
+
const cutoff = new Date(now - days * 24 * 60 * 60 * 1000).toISOString();
|
|
90
|
+
const s = await getStmts();
|
|
91
|
+
s.deleteOlderThan.run(cutoff);
|
|
92
|
+
}
|
|
93
|
+
// ---------------------------------------------------------------------------
|
|
94
|
+
// Fire-and-forget queue + drainer
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
let logQueue = [];
|
|
97
|
+
let drainTimer = null;
|
|
98
|
+
let lastOverflowWarnAt = 0;
|
|
99
|
+
let accessLogger = null;
|
|
100
|
+
function startDrainerIfNeeded() {
|
|
101
|
+
if (drainTimer)
|
|
102
|
+
return;
|
|
103
|
+
drainTimer = setInterval(() => {
|
|
104
|
+
if (logQueue.length === 0) {
|
|
105
|
+
// Even when idle, occasionally sweep retention so a long-running
|
|
106
|
+
// gatekeeper does not accumulate ancient entries.
|
|
107
|
+
maybeSweepRetention().catch((err) => {
|
|
108
|
+
accessLogger?.error({ err: err.message }, 'Failed retention sweep');
|
|
109
|
+
});
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const batch = logQueue;
|
|
113
|
+
logQueue = [];
|
|
114
|
+
persistEntries(batch)
|
|
115
|
+
.then(() => maybeSweepRetention())
|
|
116
|
+
.catch((err) => {
|
|
117
|
+
accessLogger?.error({ err: err.message }, 'Failed to persist access log batch');
|
|
118
|
+
});
|
|
119
|
+
}, LOG_DRAIN_INTERVAL_MS);
|
|
120
|
+
// Allow the process to exit cleanly during shutdown / tests.
|
|
121
|
+
drainTimer.unref?.();
|
|
122
|
+
}
|
|
123
|
+
function enqueueAccessLog(entry) {
|
|
124
|
+
if (logQueue.length >= LOG_QUEUE_MAX) {
|
|
125
|
+
// Drop oldest to bound memory. Rate-limit the warning to once per minute
|
|
126
|
+
// so a sustained overflow does not itself become a log storm.
|
|
127
|
+
logQueue.shift();
|
|
128
|
+
const now = Date.now();
|
|
129
|
+
if (now - lastOverflowWarnAt >= LOG_OVERFLOW_WARN_INTERVAL_MS) {
|
|
130
|
+
lastOverflowWarnAt = now;
|
|
131
|
+
accessLogger?.warn({ queueMax: LOG_QUEUE_MAX }, 'Access log queue full — dropping oldest entries');
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
logQueue.push(entry);
|
|
135
|
+
startDrainerIfNeeded();
|
|
136
|
+
}
|
|
137
|
+
// ---------------------------------------------------------------------------
|
|
138
|
+
// Settings I/O (gatekeeper.json stays JSON — out of migration scope)
|
|
139
|
+
// ---------------------------------------------------------------------------
|
|
140
|
+
async function saveSettings(settings) {
|
|
141
|
+
await atomicWriteJSON(settingsPath, settings, { mkdirp: true, mode: 0o600 });
|
|
142
|
+
}
|
|
143
|
+
// ---------------------------------------------------------------------------
|
|
144
|
+
// Routes
|
|
145
|
+
// ---------------------------------------------------------------------------
|
|
146
|
+
export async function diagnosticRoutes(fastify) {
|
|
147
|
+
// Adopt the Fastify logger for access-log queue diagnostics.
|
|
148
|
+
accessLogger = fastify.log;
|
|
149
|
+
// Pick up the initial retention setting from the in-memory settings cache.
|
|
150
|
+
setRetentionDays(fastify.getSettings().accessLogRetentionDays ?? null);
|
|
151
|
+
startDrainerIfNeeded();
|
|
152
|
+
// GET /api/access/check — test access for a user
|
|
153
|
+
fastify.get('/access/check', async (request, reply) => {
|
|
154
|
+
const query = request.query;
|
|
155
|
+
const username = query.username;
|
|
156
|
+
const resourceType = query.resourceType;
|
|
157
|
+
const resourceId = query.resourceId;
|
|
158
|
+
if (!username || !resourceType || !resourceId) {
|
|
159
|
+
return reply.code(400).send({
|
|
160
|
+
error: 'Missing required query params: username, resourceType, resourceId',
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
try {
|
|
164
|
+
const result = await checkAccess(username, resourceType, resourceId);
|
|
165
|
+
return result;
|
|
166
|
+
}
|
|
167
|
+
catch (err) {
|
|
168
|
+
return reply.code(500).send({ error: err.message });
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
// POST /api/cache/bust — invalidate all cached auth decisions
|
|
172
|
+
fastify.post('/cache/bust', async (_request, reply) => {
|
|
173
|
+
fastify.bustCache();
|
|
174
|
+
return { ok: true };
|
|
175
|
+
});
|
|
176
|
+
// GET /api/settings — get gatekeeper settings
|
|
177
|
+
fastify.get('/settings', async (_request, _reply) => {
|
|
178
|
+
return { settings: fastify.getSettings() };
|
|
179
|
+
});
|
|
180
|
+
// PATCH /api/settings — update gatekeeper settings
|
|
181
|
+
fastify.patch('/settings', async (request, reply) => {
|
|
182
|
+
let body;
|
|
183
|
+
try {
|
|
184
|
+
body = UpdateSettingsSchema.parse(request.body);
|
|
185
|
+
}
|
|
186
|
+
catch (err) {
|
|
187
|
+
return reply
|
|
188
|
+
.code(400)
|
|
189
|
+
.send({ error: 'Invalid request body', details: err.errors });
|
|
190
|
+
}
|
|
191
|
+
try {
|
|
192
|
+
const current = fastify.getSettings();
|
|
193
|
+
const updated = { ...current, ...body };
|
|
194
|
+
await saveSettings(updated);
|
|
195
|
+
fastify.updateSettings(updated);
|
|
196
|
+
// Honor a freshly-set retention without waiting for a daemon restart.
|
|
197
|
+
if (body.accessLogRetentionDays !== undefined) {
|
|
198
|
+
setRetentionDays(body.accessLogRetentionDays);
|
|
199
|
+
}
|
|
200
|
+
return { ok: true, settings: updated };
|
|
201
|
+
}
|
|
202
|
+
catch (err) {
|
|
203
|
+
return reply.code(500).send({ error: err.message });
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
// GET /api/access-log — get access request log (newest first, paginated)
|
|
207
|
+
fastify.get('/access-log', async (request, reply) => {
|
|
208
|
+
const query = request.query;
|
|
209
|
+
const limit = Math.max(1, Math.min(Number(query.limit) || 100, 1000));
|
|
210
|
+
const offset = Math.max(0, Number(query.offset) || 0);
|
|
211
|
+
try {
|
|
212
|
+
const s = await getStmts();
|
|
213
|
+
const totalRow = s.countAll.get();
|
|
214
|
+
const rows = s.selectPage.all(limit, offset);
|
|
215
|
+
return {
|
|
216
|
+
entries: rows.map(rowToEntry),
|
|
217
|
+
total: totalRow.n,
|
|
218
|
+
limit,
|
|
219
|
+
offset,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
catch (err) {
|
|
223
|
+
return reply.code(500).send({ error: err.message });
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
// DELETE /api/access-log — clear access request log
|
|
227
|
+
fastify.delete('/access-log', async (_request, reply) => {
|
|
228
|
+
try {
|
|
229
|
+
const s = await getStmts();
|
|
230
|
+
s.deleteAll.run();
|
|
231
|
+
return { ok: true };
|
|
232
|
+
}
|
|
233
|
+
catch (err) {
|
|
234
|
+
return reply.code(500).send({ error: err.message });
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Log a denied access request (called from authz route).
|
|
240
|
+
*
|
|
241
|
+
* Non-blocking: enqueues the entry and returns synchronously. A background
|
|
242
|
+
* drainer flushes batches every 100ms via a single SQLite transaction.
|
|
243
|
+
* Entries queued within the last 100ms of a crash may be lost — the trade-off
|
|
244
|
+
* for keeping the nginx hot path free of disk I/O.
|
|
245
|
+
*/
|
|
246
|
+
export function logAccessRequest(entry) {
|
|
247
|
+
enqueueAccessLog(entry);
|
|
248
|
+
}
|
|
249
|
+
//# sourceMappingURL=diagnostic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnostic.js","sourceRoot":"","sources":["../../../src/server/routes/diagnostic.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGxD,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,IAAI,gBAAgB,CAAC;AAC9E,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;AAEvD,MAAM,aAAa,GAAG,IAAI,CAAC;AAC3B,MAAM,qBAAqB,GAAG,GAAG,CAAC;AAClC,MAAM,6BAA6B,GAAG,MAAM,CAAC;AAC7C,MAAM,sBAAsB,GAAG,EAAE,CAAC;AAClC,0EAA0E;AAC1E,0EAA0E;AAC1E,0EAA0E;AAC1E,qBAAqB;AACrB,MAAM,2BAA2B,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEnD,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IACpC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;IAClD,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;IACzC,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;IAC5C,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;IAC5C,iBAAiB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE;IACpE,oBAAoB,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IAC5C,sBAAsB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;CACpE,CAAC,CAAC;AAiBH,IAAI,KAAK,GAAoB,IAAI,CAAC;AAElC,KAAK,UAAU,QAAQ;IACrB,IAAI,KAAK;QAAE,OAAO,KAAK,CAAC;IACxB,MAAM,EAAE,GAAG,MAAM,eAAe,EAAE,CAAC;IACnC,KAAK,GAAG;QACN,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC;;;;KAIlB,CAAC;QACF,qEAAqE;QACrE,4DAA4D;QAC5D,UAAU,EAAE,EAAE,CAAC,OAAO,CAAC,oEAAoE,CAAC;QAC5F,QAAQ,EAAE,EAAE,CAAC,OAAO,CAAC,8CAA8C,CAAC;QACpE,SAAS,EAAE,EAAE,CAAC,OAAO,CAAC,gCAAgC,CAAC;QACvD,eAAe,EAAE,EAAE,CAAC,OAAO,CAAC,oDAAoD,CAAC;QACjF,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,iBAAiB,CAAC;QACpC,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC;QAC5B,QAAQ,EAAE,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC;KACjC,CAAC;IACF,OAAO,KAAK,CAAC;AACf,CAAC;AAeD,SAAS,UAAU,CAAC,GAAW;IAC7B,OAAO;QACL,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,YAAY,EAAE,GAAG,CAAC,aAAa;QAC/B,UAAU,EAAE,GAAG,CAAC,WAAW;QAC3B,YAAY,EAAE,GAAG,CAAC,aAAa;KAChC,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,gDAAgD;AAChD,8EAA8E;AAE9E,KAAK,UAAU,cAAc,CAAC,OAA6B;IACzD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IACjC,MAAM,CAAC,GAAG,MAAM,QAAQ,EAAE,CAAC;IAC3B,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;IACd,IAAI,CAAC;QACH,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACxB,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,YAAY,CAAC,CAAC;QACtF,CAAC;QACD,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC;IACjB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,CAAC,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC;QACjB,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAED,IAAI,oBAAoB,GAAG,CAAC,CAAC;AAC7B,IAAI,qBAAqB,GAAkB,IAAI,CAAC;AAEhD,SAAS,gBAAgB,CAAC,IAAmB;IAC3C,qBAAqB,GAAG,IAAI,CAAC;AAC/B,CAAC;AAED,KAAK,UAAU,mBAAmB;IAChC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,IAAI,GAAG,GAAG,oBAAoB,GAAG,2BAA2B;QAAE,OAAO;IACrE,oBAAoB,GAAG,GAAG,CAAC;IAC3B,MAAM,IAAI,GAAG,qBAAqB,IAAI,sBAAsB,CAAC;IAC7D,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,GAAG,GAAG,IAAI,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;IACxE,MAAM,CAAC,GAAG,MAAM,QAAQ,EAAE,CAAC;IAC3B,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AAChC,CAAC;AAED,8EAA8E;AAC9E,kCAAkC;AAClC,8EAA8E;AAE9E,IAAI,QAAQ,GAAyB,EAAE,CAAC;AACxC,IAAI,UAAU,GAA0C,IAAI,CAAC;AAC7D,IAAI,kBAAkB,GAAG,CAAC,CAAC;AAC3B,IAAI,YAAY,GAA4B,IAAI,CAAC;AAEjD,SAAS,oBAAoB;IAC3B,IAAI,UAAU;QAAE,OAAO;IACvB,UAAU,GAAG,WAAW,CAAC,GAAG,EAAE;QAC5B,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,iEAAiE;YACjE,kDAAkD;YAClD,mBAAmB,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;gBAClC,YAAY,EAAE,KAAK,CAAC,EAAE,GAAG,EAAG,GAAa,CAAC,OAAO,EAAE,EAAE,wBAAwB,CAAC,CAAC;YACjF,CAAC,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QACD,MAAM,KAAK,GAAG,QAAQ,CAAC;QACvB,QAAQ,GAAG,EAAE,CAAC;QACd,cAAc,CAAC,KAAK,CAAC;aAClB,IAAI,CAAC,GAAG,EAAE,CAAC,mBAAmB,EAAE,CAAC;aACjC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;YACb,YAAY,EAAE,KAAK,CAAC,EAAE,GAAG,EAAG,GAAa,CAAC,OAAO,EAAE,EAAE,oCAAoC,CAAC,CAAC;QAC7F,CAAC,CAAC,CAAC;IACP,CAAC,EAAE,qBAAqB,CAAC,CAAC;IAC1B,6DAA6D;IAC7D,UAAU,CAAC,KAAK,EAAE,EAAE,CAAC;AACvB,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAyB;IACjD,IAAI,QAAQ,CAAC,MAAM,IAAI,aAAa,EAAE,CAAC;QACrC,yEAAyE;QACzE,8DAA8D;QAC9D,QAAQ,CAAC,KAAK,EAAE,CAAC;QACjB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,IAAI,GAAG,GAAG,kBAAkB,IAAI,6BAA6B,EAAE,CAAC;YAC9D,kBAAkB,GAAG,GAAG,CAAC;YACzB,YAAY,EAAE,IAAI,CAChB,EAAE,QAAQ,EAAE,aAAa,EAAE,EAC3B,iDAAiD,CAClD,CAAC;QACJ,CAAC;IACH,CAAC;IACD,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACrB,oBAAoB,EAAE,CAAC;AACzB,CAAC;AAED,8EAA8E;AAC9E,qEAAqE;AACrE,8EAA8E;AAE9E,KAAK,UAAU,YAAY,CAAC,QAA4B;IACtD,MAAM,eAAe,CAAC,YAAY,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AAC/E,CAAC;AAED,8EAA8E;AAC9E,SAAS;AACT,8EAA8E;AAE9E,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,OAAwB;IAC7D,6DAA6D;IAC7D,YAAY,GAAG,OAAO,CAAC,GAAuB,CAAC;IAC/C,2EAA2E;IAC3E,gBAAgB,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,sBAAsB,IAAI,IAAI,CAAC,CAAC;IACvE,oBAAoB,EAAE,CAAC;IAEvB,iDAAiD;IACjD,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,KAAK,EAAE,OAAuB,EAAE,KAAmB,EAAE,EAAE;QAClF,MAAM,KAAK,GAAG,OAAO,CAAC,KAA2C,CAAC;QAClE,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;QAChC,MAAM,YAAY,GAAG,KAAK,CAAC,YAAY,CAAC;QACxC,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;QAEpC,IAAI,CAAC,QAAQ,IAAI,CAAC,YAAY,IAAI,CAAC,UAAU,EAAE,CAAC;YAC9C,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBAC1B,KAAK,EAAE,mEAAmE;aAC3E,CAAC,CAAC;QACL,CAAC;QAED,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,QAAQ,EAAE,YAAY,EAAE,UAAU,CAAC,CAAC;YACrE,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAG,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QACjE,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,8DAA8D;IAC9D,OAAO,CAAC,IAAI,CAAC,aAAa,EAAE,KAAK,EAAE,QAAwB,EAAE,KAAmB,EAAE,EAAE;QAClF,OAAO,CAAC,SAAS,EAAE,CAAC;QACpB,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;IACtB,CAAC,CAAC,CAAC;IAEH,8CAA8C;IAC9C,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,EAAE,QAAwB,EAAE,MAAoB,EAAE,EAAE;QAChF,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,mDAAmD;IACnD,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,EAAE,OAAuB,EAAE,KAAmB,EAAE,EAAE;QAChF,IAAI,IAA0C,CAAC;QAC/C,IAAI,CAAC;YACH,IAAI,GAAG,oBAAoB,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAClD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,KAAK;iBACT,IAAI,CAAC,GAAG,CAAC;iBACT,IAAI,CAAC,EAAE,KAAK,EAAE,sBAAsB,EAAE,OAAO,EAAG,GAAkB,CAAC,MAAM,EAAE,CAAC,CAAC;QAClF,CAAC;QAED,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;YACtC,MAAM,OAAO,GAAuB,EAAE,GAAG,OAAO,EAAE,GAAG,IAAI,EAAE,CAAC;YAC5D,MAAM,YAAY,CAAC,OAAO,CAAC,CAAC;YAC5B,OAAO,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;YAChC,sEAAsE;YACtE,IAAI,IAAI,CAAC,sBAAsB,KAAK,SAAS,EAAE,CAAC;gBAC9C,gBAAgB,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;YAChD,CAAC;YACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;QACzC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAG,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QACjE,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,yEAAyE;IACzE,OAAO,CAAC,GAAG,CAAC,aAAa,EAAE,KAAK,EAAE,OAAuB,EAAE,KAAmB,EAAE,EAAE;QAChF,MAAM,KAAK,GAAG,OAAO,CAAC,KAA2C,CAAC;QAClE,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;QACtE,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;QAEtD,IAAI,CAAC;YACH,MAAM,CAAC,GAAG,MAAM,QAAQ,EAAE,CAAC;YAC3B,MAAM,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC,GAAG,EAAmB,CAAC;YACnD,MAAM,IAAI,GAAG,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAwB,CAAC;YACpE,OAAO;gBACL,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC;gBAC7B,KAAK,EAAE,QAAQ,CAAC,CAAC;gBACjB,KAAK;gBACL,MAAM;aACP,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAG,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QACjE,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,oDAAoD;IACpD,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,KAAK,EAAE,QAAwB,EAAE,KAAmB,EAAE,EAAE;QACpF,IAAI,CAAC;YACH,MAAM,CAAC,GAAG,MAAM,QAAQ,EAAE,CAAC;YAC3B,CAAC,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC;YAClB,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;QACtB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAG,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QACjE,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAyB;IACxD,gBAAgB,CAAC,KAAK,CAAC,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grants.d.ts","sourceRoot":"","sources":["../../../src/server/routes/grants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAgC,MAAM,SAAS,CAAC;AAmB7E,wBAAsB,WAAW,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAwGzE"}
|