@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,247 @@
|
|
|
1
|
+
import { PromiseChainMutex } from '@lamalibre/lamaste';
|
|
2
|
+
import { RESERVED_GROUP_NAMES, MAX_GROUPS, MAX_MEMBERS_PER_GROUP, GROUP_NAME_REGEX, MIN_GROUP_NAME_LENGTH, MAX_GROUP_NAME_LENGTH, } from './constants.js';
|
|
3
|
+
import { removeGrantsByPredicate, updateGrantsByPredicate } from './grants.js';
|
|
4
|
+
import { notifyCacheInvalidated } from './cache-bust.js';
|
|
5
|
+
import { getGatekeeperDb } from './state-db.js';
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
// Promise-chain mutex (preserved from JSON era)
|
|
8
|
+
//
|
|
9
|
+
// SQLite's BEGIN IMMEDIATE already serialises writers, but the existing
|
|
10
|
+
// public API exposes a few read-modify-write sequences (e.g. updateGroup
|
|
11
|
+
// followed by a cascading grant rename). Keeping the mutex preserves the
|
|
12
|
+
// JSON-era atomicity guarantees byte-for-byte during the storage migration.
|
|
13
|
+
// Drop as a follow-up after Step 4 (rodeo) lands.
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
const groupMutex = new PromiseChainMutex();
|
|
16
|
+
function withGroupLock(fn) {
|
|
17
|
+
return groupMutex.run(fn);
|
|
18
|
+
}
|
|
19
|
+
let stmts = null;
|
|
20
|
+
async function getStmts() {
|
|
21
|
+
if (stmts)
|
|
22
|
+
return stmts;
|
|
23
|
+
const db = await getGatekeeperDb();
|
|
24
|
+
stmts = {
|
|
25
|
+
selectAll: db.prepare('SELECT * FROM groups ORDER BY created_at'),
|
|
26
|
+
selectByName: db.prepare('SELECT * FROM groups WHERE name = ?'),
|
|
27
|
+
countAll: db.prepare('SELECT COUNT(*) AS n FROM groups'),
|
|
28
|
+
insert: db.prepare(`
|
|
29
|
+
INSERT INTO groups (name, description, members, created_at, created_by)
|
|
30
|
+
VALUES (?, ?, ?, ?, ?)
|
|
31
|
+
`),
|
|
32
|
+
updateDescription: db.prepare('UPDATE groups SET description = ? WHERE name = ?'),
|
|
33
|
+
updateName: db.prepare('UPDATE groups SET name = ? WHERE name = ?'),
|
|
34
|
+
updateMembers: db.prepare('UPDATE groups SET members = ? WHERE name = ?'),
|
|
35
|
+
deleteByName: db.prepare('DELETE FROM groups WHERE name = ?'),
|
|
36
|
+
begin: db.prepare('BEGIN IMMEDIATE'),
|
|
37
|
+
commit: db.prepare('COMMIT'),
|
|
38
|
+
rollback: db.prepare('ROLLBACK'),
|
|
39
|
+
};
|
|
40
|
+
return stmts;
|
|
41
|
+
}
|
|
42
|
+
function rowToGroup(row) {
|
|
43
|
+
return {
|
|
44
|
+
name: row.name,
|
|
45
|
+
description: row.description,
|
|
46
|
+
members: JSON.parse(row.members),
|
|
47
|
+
createdAt: row.created_at,
|
|
48
|
+
createdBy: row.created_by,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
// Validation helpers
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
function validateGroupName(name) {
|
|
55
|
+
if (name.length < MIN_GROUP_NAME_LENGTH || name.length > MAX_GROUP_NAME_LENGTH) {
|
|
56
|
+
throw Object.assign(new Error(`Group name must be ${MIN_GROUP_NAME_LENGTH}-${MAX_GROUP_NAME_LENGTH} characters`), { statusCode: 400 });
|
|
57
|
+
}
|
|
58
|
+
if (!GROUP_NAME_REGEX.test(name)) {
|
|
59
|
+
throw Object.assign(new Error('Group name must be lowercase alphanumeric with hyphens, cannot start or end with a hyphen'), { statusCode: 400 });
|
|
60
|
+
}
|
|
61
|
+
if (RESERVED_GROUP_NAMES.includes(name)) {
|
|
62
|
+
throw Object.assign(new Error(`Group name "${name}" is reserved for Authelia identity tiers`), {
|
|
63
|
+
statusCode: 400,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
// Public API — signatures preserved byte-identical to the JSON-backed version
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
export async function createGroup(name, options = {}) {
|
|
71
|
+
validateGroupName(name);
|
|
72
|
+
return withGroupLock(async () => {
|
|
73
|
+
const s = await getStmts();
|
|
74
|
+
const countRow = s.countAll.get();
|
|
75
|
+
if (countRow.n >= MAX_GROUPS) {
|
|
76
|
+
throw Object.assign(new Error(`Maximum number of groups (${MAX_GROUPS}) reached`), {
|
|
77
|
+
statusCode: 503,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
const existing = s.selectByName.get(name);
|
|
81
|
+
if (existing) {
|
|
82
|
+
throw Object.assign(new Error(`Group "${name}" already exists`), { statusCode: 409 });
|
|
83
|
+
}
|
|
84
|
+
const group = {
|
|
85
|
+
name,
|
|
86
|
+
description: options.description ?? '',
|
|
87
|
+
members: [],
|
|
88
|
+
createdAt: new Date().toISOString(),
|
|
89
|
+
createdBy: options.createdBy ?? 'admin',
|
|
90
|
+
};
|
|
91
|
+
s.begin.run();
|
|
92
|
+
try {
|
|
93
|
+
s.insert.run(group.name, group.description, JSON.stringify(group.members), group.createdAt, group.createdBy);
|
|
94
|
+
s.commit.run();
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
s.rollback.run();
|
|
98
|
+
throw err;
|
|
99
|
+
}
|
|
100
|
+
notifyCacheInvalidated();
|
|
101
|
+
return group;
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
export async function listGroups() {
|
|
105
|
+
const s = await getStmts();
|
|
106
|
+
const rows = s.selectAll.all();
|
|
107
|
+
return rows.map(rowToGroup);
|
|
108
|
+
}
|
|
109
|
+
export async function getGroup(name) {
|
|
110
|
+
const s = await getStmts();
|
|
111
|
+
const row = s.selectByName.get(name);
|
|
112
|
+
return row ? rowToGroup(row) : null;
|
|
113
|
+
}
|
|
114
|
+
export async function updateGroup(name, updates) {
|
|
115
|
+
return withGroupLock(async () => {
|
|
116
|
+
const s = await getStmts();
|
|
117
|
+
const existingRow = s.selectByName.get(name);
|
|
118
|
+
if (!existingRow) {
|
|
119
|
+
throw Object.assign(new Error(`Group "${name}" not found`), { statusCode: 404 });
|
|
120
|
+
}
|
|
121
|
+
const group = rowToGroup(existingRow);
|
|
122
|
+
let cascadingRename = null;
|
|
123
|
+
if (updates.name !== undefined && updates.name !== name) {
|
|
124
|
+
validateGroupName(updates.name);
|
|
125
|
+
const conflict = s.selectByName.get(updates.name);
|
|
126
|
+
if (conflict) {
|
|
127
|
+
throw Object.assign(new Error(`Group "${updates.name}" already exists`), {
|
|
128
|
+
statusCode: 409,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
cascadingRename = updates.name;
|
|
132
|
+
}
|
|
133
|
+
if (updates.description !== undefined) {
|
|
134
|
+
group.description = updates.description;
|
|
135
|
+
}
|
|
136
|
+
s.begin.run();
|
|
137
|
+
try {
|
|
138
|
+
if (updates.description !== undefined) {
|
|
139
|
+
s.updateDescription.run(group.description, name);
|
|
140
|
+
}
|
|
141
|
+
if (cascadingRename !== null) {
|
|
142
|
+
s.updateName.run(cascadingRename, name);
|
|
143
|
+
group.name = cascadingRename;
|
|
144
|
+
}
|
|
145
|
+
s.commit.run();
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
s.rollback.run();
|
|
149
|
+
throw err;
|
|
150
|
+
}
|
|
151
|
+
if (cascadingRename !== null) {
|
|
152
|
+
// Cascade rename to grants (uses grantLock for safe concurrent access).
|
|
153
|
+
// Runs outside the SQL transaction because grants writes use their own
|
|
154
|
+
// mutex + transaction; nesting them would deadlock.
|
|
155
|
+
const newName = cascadingRename;
|
|
156
|
+
await updateGrantsByPredicate((g) => g.principalType === 'group' && g.principalId === name, (g) => {
|
|
157
|
+
g.principalId = newName;
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
notifyCacheInvalidated();
|
|
161
|
+
return group;
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
export async function deleteGroup(name) {
|
|
165
|
+
validateGroupName(name);
|
|
166
|
+
return withGroupLock(async () => {
|
|
167
|
+
const s = await getStmts();
|
|
168
|
+
const existing = s.selectByName.get(name);
|
|
169
|
+
if (!existing) {
|
|
170
|
+
throw Object.assign(new Error(`Group "${name}" not found`), { statusCode: 404 });
|
|
171
|
+
}
|
|
172
|
+
s.begin.run();
|
|
173
|
+
try {
|
|
174
|
+
s.deleteByName.run(name);
|
|
175
|
+
s.commit.run();
|
|
176
|
+
}
|
|
177
|
+
catch (err) {
|
|
178
|
+
s.rollback.run();
|
|
179
|
+
throw err;
|
|
180
|
+
}
|
|
181
|
+
notifyCacheInvalidated();
|
|
182
|
+
// Auto-revoke all grants referencing this group (uses grantLock)
|
|
183
|
+
const deletedGrants = await removeGrantsByPredicate((g) => g.principalType === 'group' && g.principalId === name);
|
|
184
|
+
return { deletedGrants };
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
export async function addMembers(groupName, usernames) {
|
|
188
|
+
return withGroupLock(async () => {
|
|
189
|
+
const s = await getStmts();
|
|
190
|
+
const row = s.selectByName.get(groupName);
|
|
191
|
+
if (!row) {
|
|
192
|
+
throw Object.assign(new Error(`Group "${groupName}" not found`), { statusCode: 404 });
|
|
193
|
+
}
|
|
194
|
+
const group = rowToGroup(row);
|
|
195
|
+
for (const username of usernames) {
|
|
196
|
+
if (!group.members.includes(username)) {
|
|
197
|
+
if (group.members.length >= MAX_MEMBERS_PER_GROUP) {
|
|
198
|
+
throw Object.assign(new Error(`Maximum members per group (${MAX_MEMBERS_PER_GROUP}) reached`), { statusCode: 503 });
|
|
199
|
+
}
|
|
200
|
+
group.members.push(username);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
s.begin.run();
|
|
204
|
+
try {
|
|
205
|
+
s.updateMembers.run(JSON.stringify(group.members), groupName);
|
|
206
|
+
s.commit.run();
|
|
207
|
+
}
|
|
208
|
+
catch (err) {
|
|
209
|
+
s.rollback.run();
|
|
210
|
+
throw err;
|
|
211
|
+
}
|
|
212
|
+
notifyCacheInvalidated();
|
|
213
|
+
return group;
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
export async function removeMembers(groupName, usernames) {
|
|
217
|
+
return withGroupLock(async () => {
|
|
218
|
+
const s = await getStmts();
|
|
219
|
+
const row = s.selectByName.get(groupName);
|
|
220
|
+
if (!row) {
|
|
221
|
+
throw Object.assign(new Error(`Group "${groupName}" not found`), { statusCode: 404 });
|
|
222
|
+
}
|
|
223
|
+
const group = rowToGroup(row);
|
|
224
|
+
const removeSet = new Set(usernames);
|
|
225
|
+
group.members = group.members.filter((m) => !removeSet.has(m));
|
|
226
|
+
s.begin.run();
|
|
227
|
+
try {
|
|
228
|
+
s.updateMembers.run(JSON.stringify(group.members), groupName);
|
|
229
|
+
s.commit.run();
|
|
230
|
+
}
|
|
231
|
+
catch (err) {
|
|
232
|
+
s.rollback.run();
|
|
233
|
+
throw err;
|
|
234
|
+
}
|
|
235
|
+
notifyCacheInvalidated();
|
|
236
|
+
return group;
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
export async function getGroupsForUser(username) {
|
|
240
|
+
const s = await getStmts();
|
|
241
|
+
const rows = s.selectAll.all();
|
|
242
|
+
return rows
|
|
243
|
+
.map(rowToGroup)
|
|
244
|
+
.filter((g) => g.members.includes(username))
|
|
245
|
+
.map((g) => g.name);
|
|
246
|
+
}
|
|
247
|
+
//# sourceMappingURL=groups.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"groups.js","sourceRoot":"","sources":["../../src/lib/groups.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { GatekeeperLogger } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Migrate legacy `user-plugin-access.json` grants to the new
|
|
4
|
+
* `access-grants.json` format. Idempotent — skips if already migrated.
|
|
5
|
+
*
|
|
6
|
+
* Mapping:
|
|
7
|
+
* - username → principalType: 'user', principalId: username
|
|
8
|
+
* - pluginName → resourceType: 'plugin', resourceId: pluginName
|
|
9
|
+
* - target → context: { target }
|
|
10
|
+
*
|
|
11
|
+
* The legacy file is renamed to `.migrated` after successful migration.
|
|
12
|
+
*
|
|
13
|
+
* @param logger - Logger for progress reporting
|
|
14
|
+
* @returns Number of grants migrated
|
|
15
|
+
*/
|
|
16
|
+
export declare function migrateFromLegacy(logger?: GatekeeperLogger): Promise<number>;
|
|
17
|
+
//# sourceMappingURL=migration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration.d.ts","sourceRoot":"","sources":["../../src/lib/migration.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAqBnD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAsElF"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { readFile, rename } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { DEFAULT_DATA_DIR } from './constants.js';
|
|
4
|
+
import { createGrant } from './grants.js';
|
|
5
|
+
const dataDir = process.env.LAMALIBRE_LAMASTE_DATA_DIR ?? DEFAULT_DATA_DIR;
|
|
6
|
+
const LEGACY_FILE = 'user-plugin-access.json';
|
|
7
|
+
const MIGRATED_SUFFIX = '.migrated';
|
|
8
|
+
/**
|
|
9
|
+
* Migrate legacy `user-plugin-access.json` grants to the new
|
|
10
|
+
* `access-grants.json` format. Idempotent — skips if already migrated.
|
|
11
|
+
*
|
|
12
|
+
* Mapping:
|
|
13
|
+
* - username → principalType: 'user', principalId: username
|
|
14
|
+
* - pluginName → resourceType: 'plugin', resourceId: pluginName
|
|
15
|
+
* - target → context: { target }
|
|
16
|
+
*
|
|
17
|
+
* The legacy file is renamed to `.migrated` after successful migration.
|
|
18
|
+
*
|
|
19
|
+
* @param logger - Logger for progress reporting
|
|
20
|
+
* @returns Number of grants migrated
|
|
21
|
+
*/
|
|
22
|
+
export async function migrateFromLegacy(logger) {
|
|
23
|
+
const legacyPath = path.join(dataDir, LEGACY_FILE);
|
|
24
|
+
let raw;
|
|
25
|
+
try {
|
|
26
|
+
raw = await readFile(legacyPath, 'utf-8');
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
if (err.code === 'ENOENT') {
|
|
30
|
+
logger?.info('No legacy user-plugin-access.json found, skipping migration');
|
|
31
|
+
return 0;
|
|
32
|
+
}
|
|
33
|
+
throw err;
|
|
34
|
+
}
|
|
35
|
+
const parsed = JSON.parse(raw);
|
|
36
|
+
if (parsed === null || typeof parsed !== 'object' || !('grants' in parsed)) {
|
|
37
|
+
logger?.warn('Legacy file has unexpected format, skipping migration');
|
|
38
|
+
return 0;
|
|
39
|
+
}
|
|
40
|
+
const legacy = parsed;
|
|
41
|
+
if (!Array.isArray(legacy.grants) || legacy.grants.length === 0) {
|
|
42
|
+
logger?.info('Legacy file has no grants, renaming to .migrated');
|
|
43
|
+
await rename(legacyPath, legacyPath + MIGRATED_SUFFIX);
|
|
44
|
+
return 0;
|
|
45
|
+
}
|
|
46
|
+
let migrated = 0;
|
|
47
|
+
for (const legacyGrant of legacy.grants) {
|
|
48
|
+
try {
|
|
49
|
+
// Preserve consumption state from the legacy file so previously-used
|
|
50
|
+
// local-plugin grants do not become re-usable post-migration.
|
|
51
|
+
await createGrant({
|
|
52
|
+
principalType: 'user',
|
|
53
|
+
principalId: legacyGrant.username,
|
|
54
|
+
resourceType: 'plugin',
|
|
55
|
+
resourceId: legacyGrant.pluginName,
|
|
56
|
+
context: { target: legacyGrant.target },
|
|
57
|
+
}, {
|
|
58
|
+
used: legacyGrant.used,
|
|
59
|
+
createdAt: legacyGrant.createdAt,
|
|
60
|
+
usedAt: legacyGrant.usedAt,
|
|
61
|
+
});
|
|
62
|
+
migrated++;
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
// Skip duplicates (already migrated or duplicate in source)
|
|
66
|
+
const error = err;
|
|
67
|
+
if (error.statusCode === 409) {
|
|
68
|
+
logger?.info(`Skipping duplicate grant for ${legacyGrant.username} → ${legacyGrant.pluginName}`);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
throw err;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Rename legacy file to mark as migrated
|
|
75
|
+
await rename(legacyPath, legacyPath + MIGRATED_SUFFIX);
|
|
76
|
+
logger?.info({ migrated, total: legacy.grants.length }, `Migrated ${migrated} of ${legacy.grants.length} legacy grants`);
|
|
77
|
+
return migrated;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=migration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration.js","sourceRoot":"","sources":["../../src/lib/migration.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
CREATE TABLE access_grants (
|
|
2
|
+
grant_id TEXT PRIMARY KEY, -- UUID
|
|
3
|
+
principal_type TEXT NOT NULL, -- 'user' | 'group'
|
|
4
|
+
principal_id TEXT NOT NULL,
|
|
5
|
+
resource_type TEXT NOT NULL, -- 'tunnel' | 'plugin' | extensible
|
|
6
|
+
resource_id TEXT NOT NULL,
|
|
7
|
+
context TEXT NOT NULL DEFAULT '{}', -- JSON object
|
|
8
|
+
used INTEGER NOT NULL DEFAULT 0,
|
|
9
|
+
created_at TEXT NOT NULL,
|
|
10
|
+
used_at TEXT
|
|
11
|
+
);
|
|
12
|
+
CREATE INDEX idx_grants_principal ON access_grants(principal_type, principal_id);
|
|
13
|
+
CREATE INDEX idx_grants_resource ON access_grants(resource_type, resource_id);
|
|
14
|
+
CREATE INDEX idx_grants_used_used_at ON access_grants(used, used_at);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
CREATE TABLE access_request_log (
|
|
2
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
3
|
+
timestamp TEXT NOT NULL,
|
|
4
|
+
username TEXT NOT NULL,
|
|
5
|
+
resource_type TEXT NOT NULL,
|
|
6
|
+
resource_id TEXT NOT NULL,
|
|
7
|
+
resource_fqdn TEXT NOT NULL
|
|
8
|
+
);
|
|
9
|
+
CREATE INDEX idx_access_log_timestamp ON access_request_log(timestamp);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
CREATE TABLE access_grants (
|
|
2
|
+
grant_id TEXT PRIMARY KEY, -- UUID
|
|
3
|
+
principal_type TEXT NOT NULL, -- 'user' | 'group'
|
|
4
|
+
principal_id TEXT NOT NULL,
|
|
5
|
+
resource_type TEXT NOT NULL, -- 'tunnel' | 'plugin' | extensible
|
|
6
|
+
resource_id TEXT NOT NULL,
|
|
7
|
+
context TEXT NOT NULL DEFAULT '{}', -- JSON object
|
|
8
|
+
used INTEGER NOT NULL DEFAULT 0,
|
|
9
|
+
created_at TEXT NOT NULL,
|
|
10
|
+
used_at TEXT
|
|
11
|
+
);
|
|
12
|
+
CREATE INDEX idx_grants_principal ON access_grants(principal_type, principal_id);
|
|
13
|
+
CREATE INDEX idx_grants_resource ON access_grants(resource_type, resource_id);
|
|
14
|
+
CREATE INDEX idx_grants_used_used_at ON access_grants(used, used_at);
|
package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0003_access_request_log.sql
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
CREATE TABLE access_request_log (
|
|
2
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
3
|
+
timestamp TEXT NOT NULL,
|
|
4
|
+
username TEXT NOT NULL,
|
|
5
|
+
resource_type TEXT NOT NULL,
|
|
6
|
+
resource_id TEXT NOT NULL,
|
|
7
|
+
resource_fqdn TEXT NOT NULL
|
|
8
|
+
);
|
|
9
|
+
CREATE INDEX idx_access_log_timestamp ON access_request_log(timestamp);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { DatabaseSync } from 'node:sqlite';
|
|
2
|
+
/**
|
|
3
|
+
* Returns the singleton `gatekeeper.db` handle for lamaste-gatekeeper.
|
|
4
|
+
* Lazy-opens on first call; subsequent calls return the cached handle. Safe to
|
|
5
|
+
* call concurrently from multiple modules at startup — the open is wrapped in
|
|
6
|
+
* a promise singleton so only one open ever races.
|
|
7
|
+
*/
|
|
8
|
+
export declare function getGatekeeperDb(): Promise<DatabaseSync>;
|
|
9
|
+
/**
|
|
10
|
+
* Apply every pending migration in `migrationsDir` against `db`. Idempotent —
|
|
11
|
+
* the second call is a no-op once `schema_migrations` has caught up. Each
|
|
12
|
+
* migration runs inside `BEGIN IMMEDIATE` … `COMMIT`; a failure rolls the
|
|
13
|
+
* statement back and rethrows with the migration filename in the message.
|
|
14
|
+
*/
|
|
15
|
+
export declare function ensureMigrations(db: DatabaseSync, migrationsDir: string): Promise<void>;
|
|
16
|
+
//# sourceMappingURL=state-db.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-db.d.ts","sourceRoot":"","sources":["../../src/lib/state-db.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AA+ChD;;;;;GAKG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,YAAY,CAAC,CAS7D;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CAAC,EAAE,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA2C7F"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// Foundation for SQLite-backed state in lamaste-gatekeeper. Owns the singleton
|
|
2
|
+
// DB handle, PRAGMA tuning, file-permission tightening, and the forward-only
|
|
3
|
+
// numbered-migrations runner. Domain modules consume this via
|
|
4
|
+
// `getGatekeeperDb()` and never instantiate `DatabaseSync` themselves.
|
|
5
|
+
//
|
|
6
|
+
// Mirrors the serverd foundation at packages/server/daemon/src/lib/state-db.js.
|
|
7
|
+
// Duplicated rather than hoisted to core per design note §2 — hoist once both
|
|
8
|
+
// copies are green and identical in shape.
|
|
9
|
+
import { chmod, readdir, readFile } from 'node:fs/promises';
|
|
10
|
+
import { existsSync } from 'node:fs';
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
13
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
const DATA_DIR_ENV = 'LAMALIBRE_LAMASTE_DATA_DIR';
|
|
15
|
+
const DEFAULT_DATA_DIR = '/etc/lamalibre/lamaste';
|
|
16
|
+
const GATEKEEPER_DB_FILENAME = 'gatekeeper.db';
|
|
17
|
+
// Migrations live as raw .sql files under src/lib/migrations/gatekeeper/.
|
|
18
|
+
// `tsc` does not copy non-TS assets into `dist/`, so at runtime the compiled
|
|
19
|
+
// state-db.js sits in `dist/lib/` while the migration sources sit in
|
|
20
|
+
// `src/lib/migrations/gatekeeper/` (one level up + over). We prefer the
|
|
21
|
+
// `dist`-relative location so a future build-time copy step (or a published
|
|
22
|
+
// package that bundles SQL into dist) just works, and we fall back to the
|
|
23
|
+
// `src`-relative location when running from a checkout where dist doesn't
|
|
24
|
+
// have its own copy. The fallback walks up looking for the first ancestor
|
|
25
|
+
// that contains a `package.json` and reaches into its `src/lib/migrations/...`
|
|
26
|
+
// from there, mirroring how serverd's pure-JS foundation finds the same path
|
|
27
|
+
// via `__dirname`.
|
|
28
|
+
const GATEKEEPER_MIGRATIONS_DIR = resolveMigrationsDir();
|
|
29
|
+
function resolveMigrationsDir() {
|
|
30
|
+
const distRelative = path.join(__dirname, 'migrations', 'gatekeeper');
|
|
31
|
+
if (existsSync(distRelative))
|
|
32
|
+
return distRelative;
|
|
33
|
+
// Walk up from __dirname looking for the package root (the directory whose
|
|
34
|
+
// parent contains `package.json`). This handles the dev/test path where
|
|
35
|
+
// `tsc` emitted to `dist/` but did not copy the SQL files alongside.
|
|
36
|
+
let cursor = __dirname;
|
|
37
|
+
for (let i = 0; i < 6; i += 1) {
|
|
38
|
+
const candidatePkg = path.join(cursor, 'package.json');
|
|
39
|
+
if (existsSync(candidatePkg)) {
|
|
40
|
+
const srcCandidate = path.join(cursor, 'src', 'lib', 'migrations', 'gatekeeper');
|
|
41
|
+
if (existsSync(srcCandidate))
|
|
42
|
+
return srcCandidate;
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
const parent = path.dirname(cursor);
|
|
46
|
+
if (parent === cursor)
|
|
47
|
+
break;
|
|
48
|
+
cursor = parent;
|
|
49
|
+
}
|
|
50
|
+
return distRelative; // fall through; ensureMigrations will ENOENT-shortcut
|
|
51
|
+
}
|
|
52
|
+
const MIGRATION_FILENAME_RE = /^\d{4}_[a-z0-9_]+\.sql$/;
|
|
53
|
+
let dbPromise = null;
|
|
54
|
+
/**
|
|
55
|
+
* Returns the singleton `gatekeeper.db` handle for lamaste-gatekeeper.
|
|
56
|
+
* Lazy-opens on first call; subsequent calls return the cached handle. Safe to
|
|
57
|
+
* call concurrently from multiple modules at startup — the open is wrapped in
|
|
58
|
+
* a promise singleton so only one open ever races.
|
|
59
|
+
*/
|
|
60
|
+
export async function getGatekeeperDb() {
|
|
61
|
+
if (!dbPromise) {
|
|
62
|
+
const dataDir = process.env[DATA_DIR_ENV] ?? DEFAULT_DATA_DIR;
|
|
63
|
+
dbPromise = openManagedDb({
|
|
64
|
+
path: path.join(dataDir, GATEKEEPER_DB_FILENAME),
|
|
65
|
+
migrationsDir: GATEKEEPER_MIGRATIONS_DIR,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return dbPromise;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Apply every pending migration in `migrationsDir` against `db`. Idempotent —
|
|
72
|
+
* the second call is a no-op once `schema_migrations` has caught up. Each
|
|
73
|
+
* migration runs inside `BEGIN IMMEDIATE` … `COMMIT`; a failure rolls the
|
|
74
|
+
* statement back and rethrows with the migration filename in the message.
|
|
75
|
+
*/
|
|
76
|
+
export async function ensureMigrations(db, migrationsDir) {
|
|
77
|
+
db.exec(`CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
78
|
+
id INTEGER PRIMARY KEY,
|
|
79
|
+
name TEXT NOT NULL UNIQUE,
|
|
80
|
+
applied_at TEXT NOT NULL
|
|
81
|
+
)`);
|
|
82
|
+
const applied = new Set(db
|
|
83
|
+
.prepare('SELECT name FROM schema_migrations')
|
|
84
|
+
.all()
|
|
85
|
+
.map((r) => r.name));
|
|
86
|
+
let entries;
|
|
87
|
+
try {
|
|
88
|
+
entries = await readdir(migrationsDir);
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
if (err.code === 'ENOENT')
|
|
92
|
+
return;
|
|
93
|
+
throw err;
|
|
94
|
+
}
|
|
95
|
+
const files = entries.filter((f) => MIGRATION_FILENAME_RE.test(f)).sort();
|
|
96
|
+
for (const file of files) {
|
|
97
|
+
if (applied.has(file))
|
|
98
|
+
continue;
|
|
99
|
+
const sql = await readFile(path.join(migrationsDir, file), 'utf-8');
|
|
100
|
+
const id = Number(file.slice(0, 4));
|
|
101
|
+
db.exec('BEGIN IMMEDIATE');
|
|
102
|
+
try {
|
|
103
|
+
db.exec(sql);
|
|
104
|
+
db.prepare('INSERT INTO schema_migrations (id, name, applied_at) VALUES (?, ?, ?)').run(id, file, new Date().toISOString());
|
|
105
|
+
db.exec('COMMIT');
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
db.exec('ROLLBACK');
|
|
109
|
+
throw new Error(`Migration ${file} failed: ${err.message}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Open `path`, apply PRAGMAs, tighten file modes, and run any pending
|
|
115
|
+
* migrations from `migrationsDir`. Single source of truth for DB lifecycle —
|
|
116
|
+
* `getGatekeeperDb()` is a thin wrapper that supplies gatekeeper's
|
|
117
|
+
* path/migrations.
|
|
118
|
+
*
|
|
119
|
+
* `node:sqlite` is dynamically imported so `boot.ts`'s `process.on('warning')`
|
|
120
|
+
* filter (installed during user-code evaluation) catches the
|
|
121
|
+
* ExperimentalWarning. A static import would emit it during ESM link.
|
|
122
|
+
*/
|
|
123
|
+
async function openManagedDb({ path: dbPath, migrationsDir, }) {
|
|
124
|
+
const { DatabaseSync } = await import('node:sqlite');
|
|
125
|
+
const db = new DatabaseSync(dbPath);
|
|
126
|
+
// PRAGMA order matters: journal_mode must be set before any write that
|
|
127
|
+
// would lock the rollback journal. cache_size = -2048 caps the page cache
|
|
128
|
+
// at 2 MiB (negative = KiB). synchronous = NORMAL is safe with WAL and
|
|
129
|
+
// skips the per-commit fsync.
|
|
130
|
+
db.exec(`
|
|
131
|
+
PRAGMA journal_mode = WAL;
|
|
132
|
+
PRAGMA synchronous = NORMAL;
|
|
133
|
+
PRAGMA foreign_keys = ON;
|
|
134
|
+
PRAGMA cache_size = -2048;
|
|
135
|
+
PRAGMA temp_store = MEMORY;
|
|
136
|
+
PRAGMA busy_timeout = 5000;
|
|
137
|
+
`);
|
|
138
|
+
// 0o600 on the DB plus its WAL/SHM sidecars (which appear lazily on first
|
|
139
|
+
// write — guard with existsSync). Best-effort: a failure here is logged by
|
|
140
|
+
// the caller's surrounding context, not fatal.
|
|
141
|
+
try {
|
|
142
|
+
await chmod(dbPath, 0o600);
|
|
143
|
+
if (existsSync(`${dbPath}-wal`))
|
|
144
|
+
await chmod(`${dbPath}-wal`, 0o600);
|
|
145
|
+
if (existsSync(`${dbPath}-shm`))
|
|
146
|
+
await chmod(`${dbPath}-shm`, 0o600);
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
// best-effort
|
|
150
|
+
}
|
|
151
|
+
await ensureMigrations(db, migrationsDir);
|
|
152
|
+
return db;
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=state-db.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-db.js","sourceRoot":"","sources":["../../src/lib/state-db.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { AccessRequestTemplates, TemplateOptions } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Generate pre-filled message templates for requesting access to a resource.
|
|
4
|
+
*
|
|
5
|
+
* @param username - The authenticated user's username
|
|
6
|
+
* @param resourceName - Human-readable resource name (e.g., FQDN or plugin name)
|
|
7
|
+
* @param options - Optional admin contact info and domain
|
|
8
|
+
* @returns Templates for email, Slack, Teams, WhatsApp, and generic
|
|
9
|
+
*/
|
|
10
|
+
export declare function getAccessRequestTemplates(username: string, resourceName: string, options?: TemplateOptions): AccessRequestTemplates;
|
|
11
|
+
/**
|
|
12
|
+
* Build an HTML page for the access-request denial page.
|
|
13
|
+
* Server-rendered with inline CSS, no external dependencies.
|
|
14
|
+
*
|
|
15
|
+
* @param username - The authenticated user's username
|
|
16
|
+
* @param resourceFqdn - The tunnel FQDN the user tried to access
|
|
17
|
+
* @param options - Optional admin contact info
|
|
18
|
+
* @returns Full HTML page string
|
|
19
|
+
*/
|
|
20
|
+
export declare function buildAccessRequestPage(username: string, resourceFqdn: string, options?: TemplateOptions): string;
|
|
21
|
+
//# sourceMappingURL=templates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../src/lib/templates.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE1E;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,eAAe,GACxB,sBAAsB,CAmBxB;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,eAAe,GACxB,MAAM,CAmGR"}
|