@lamalibre/create-lamaste 4.6.0 → 4.7.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.
Files changed (149) hide show
  1. package/package.json +2 -2
  2. package/scripts/bundle-vendor.js +20 -3
  3. package/scripts/lamaste-priv +22 -3
  4. package/scripts/pin-release.js +98 -0
  5. package/src/index.js +8 -1
  6. package/src/lib/env.js +19 -0
  7. package/src/lib/service-config.js +7 -0
  8. package/src/tasks/redeploy.js +8 -0
  9. package/vendor/docs/00-introduction/how-it-works.md +2 -2
  10. package/vendor/docs/00-introduction/quickstart.md +5 -5
  11. package/vendor/docs/00-introduction/what-is-lamaste.md +5 -5
  12. package/vendor/docs/01-concepts/authentication.md +6 -2
  13. package/vendor/docs/01-concepts/authorization.md +4 -0
  14. package/vendor/docs/01-concepts/certificates.md +3 -3
  15. package/vendor/docs/01-concepts/dns-and-domains.md +9 -0
  16. package/vendor/docs/01-concepts/mtls.md +2 -2
  17. package/vendor/docs/01-concepts/security-model.md +28 -3
  18. package/vendor/docs/01-concepts/tickets.md +2 -2
  19. package/vendor/docs/01-concepts/tunneling.md +4 -4
  20. package/vendor/docs/02-guides/agent-setup.md +5 -5
  21. package/vendor/docs/02-guides/certificate-management.md +2 -2
  22. package/vendor/docs/02-guides/cloud-provisioning.md +3 -3
  23. package/vendor/docs/02-guides/desktop-app-setup.md +10 -10
  24. package/vendor/docs/02-guides/installation.md +3 -3
  25. package/vendor/docs/02-guides/managing-users.md +5 -11
  26. package/vendor/docs/02-guides/onboarding.md +2 -2
  27. package/vendor/docs/02-guides/second-domain.md +82 -0
  28. package/vendor/docs/02-guides/standalone-plugin-with-tickets.md +2 -2
  29. package/vendor/docs/03-architecture/installer-flow.md +2 -2
  30. package/vendor/docs/03-architecture/installer.md +4 -4
  31. package/vendor/docs/03-architecture/management-flow.md +1 -1
  32. package/vendor/docs/03-architecture/nginx-configuration.md +15 -5
  33. package/vendor/docs/03-architecture/overview.md +5 -5
  34. package/vendor/docs/03-architecture/panel-client.md +6 -6
  35. package/vendor/docs/03-architecture/panel-server.md +2 -2
  36. package/vendor/docs/03-architecture/state-management.md +3 -3
  37. package/vendor/docs/03-architecture/system-overview.md +1 -1
  38. package/vendor/docs/04-api-reference/agent-hosted-plugins.md +17 -13
  39. package/vendor/docs/04-api-reference/app-host.md +3 -3
  40. package/vendor/docs/04-api-reference/domains.md +192 -0
  41. package/vendor/docs/04-api-reference/gatekeeper.md +35 -7
  42. package/vendor/docs/04-api-reference/lifecycle-hooks.md +4 -4
  43. package/vendor/docs/04-api-reference/onboarding.md +1 -1
  44. package/vendor/docs/04-api-reference/overview.md +9 -0
  45. package/vendor/docs/04-api-reference/registry-and-oidc.md +26 -13
  46. package/vendor/docs/04-api-reference/sites.md +5 -0
  47. package/vendor/docs/04-api-reference/tunnels.md +15 -10
  48. package/vendor/docs/04-api-reference/users.md +41 -29
  49. package/vendor/docs/05-operations/backup-and-restore.md +1 -1
  50. package/vendor/docs/05-operations/uninstalling.md +1 -1
  51. package/vendor/docs/05-operations/upgrades.md +6 -6
  52. package/vendor/docs/06-reference/config-files.md +1 -1
  53. package/vendor/docs/06-reference/glossary.md +3 -3
  54. package/vendor/docs/06-reference/installer-flags.md +13 -13
  55. package/vendor/docs/06-reference/ports-and-services.md +1 -1
  56. package/vendor/docs/06-reference/troubleshooting.md +8 -8
  57. package/vendor/docs/_index.json +105 -19
  58. package/vendor/docs/external-members-design.md +816 -0
  59. package/vendor/docs/gatekeeper-design.md +8 -8
  60. package/vendor/docs/identity-directory-design.md +3 -3
  61. package/vendor/docs/multi-domain-relay-design.md +836 -0
  62. package/vendor/docs/package.json +1 -1
  63. package/vendor/docs/todo/lib-cli-desktop-refactor.md +27 -27
  64. package/vendor/gatekeeper/dist/cli/index.js +0 -0
  65. package/vendor/gatekeeper/dist/lib/grants.d.ts.map +1 -1
  66. package/vendor/gatekeeper/dist/lib/grants.js +20 -13
  67. package/vendor/gatekeeper/dist/lib/grants.js.map +1 -1
  68. package/vendor/gatekeeper/dist/lib/migrations/gatekeeper/0004_unconsume_access_grants.sql +8 -0
  69. package/vendor/gatekeeper/dist/lib/secret.d.ts +6 -0
  70. package/vendor/gatekeeper/dist/lib/secret.d.ts.map +1 -0
  71. package/vendor/gatekeeper/dist/lib/secret.js +12 -0
  72. package/vendor/gatekeeper/dist/lib/secret.js.map +1 -0
  73. package/vendor/gatekeeper/dist/lib/templates.d.ts +20 -0
  74. package/vendor/gatekeeper/dist/lib/templates.d.ts.map +1 -1
  75. package/vendor/gatekeeper/dist/lib/templates.js +80 -14
  76. package/vendor/gatekeeper/dist/lib/templates.js.map +1 -1
  77. package/vendor/gatekeeper/dist/server/index.d.ts +5 -1
  78. package/vendor/gatekeeper/dist/server/index.d.ts.map +1 -1
  79. package/vendor/gatekeeper/dist/server/index.js +28 -2
  80. package/vendor/gatekeeper/dist/server/index.js.map +1 -1
  81. package/vendor/gatekeeper/dist/server/routes/authz.d.ts.map +1 -1
  82. package/vendor/gatekeeper/dist/server/routes/authz.js +225 -36
  83. package/vendor/gatekeeper/dist/server/routes/authz.js.map +1 -1
  84. package/vendor/gatekeeper/dist/server/routes/diagnostic.d.ts.map +1 -1
  85. package/vendor/gatekeeper/dist/server/routes/diagnostic.js +5 -2
  86. package/vendor/gatekeeper/dist/server/routes/diagnostic.js.map +1 -1
  87. package/vendor/gatekeeper/package.json +2 -2
  88. package/vendor/server/bin/lamaste-server.js +7 -0
  89. package/vendor/server/package.json +2 -2
  90. package/vendor/server/src/commands/domains.js +214 -0
  91. package/vendor/server/src/commands/plugin-identity.js +2 -16
  92. package/vendor/server/src/commands/plugins.js +112 -8
  93. package/vendor/server/src/commands/registry-credential.js +10 -3
  94. package/vendor/server/src/commands/sites.js +3 -0
  95. package/vendor/server/src/commands/status.js +19 -0
  96. package/vendor/server/src/commands/tunnels.js +4 -1
  97. package/vendor/server/src/commands/users.js +8 -19
  98. package/vendor/server/src/domains.js +92 -0
  99. package/vendor/server/src/fail.js +21 -0
  100. package/vendor/server/src/oidc-client-grants.js +63 -0
  101. package/vendor/server/src/panel-api.js +15 -0
  102. package/vendor/server-ui/dist/assets/index-B8Tv_ocF.js +84 -0
  103. package/vendor/server-ui/dist/assets/{index-BZwQ_aQc.css → index-Cb-ADsbB.css} +1 -1
  104. package/vendor/server-ui/dist/index.html +2 -2
  105. package/vendor/serverd/package.json +2 -2
  106. package/vendor/serverd/src/index.js +42 -16
  107. package/vendor/serverd/src/lib/app-error.js +24 -0
  108. package/vendor/serverd/src/lib/audience.js +12 -3
  109. package/vendor/serverd/src/lib/authelia.js +341 -35
  110. package/vendor/serverd/src/lib/directory.js +67 -52
  111. package/vendor/serverd/src/lib/domains.js +1792 -0
  112. package/vendor/serverd/src/lib/gatekeeper-client.js +20 -4
  113. package/vendor/serverd/src/lib/identity-sync.js +35 -0
  114. package/vendor/serverd/src/lib/migrations/state/0016_domains.sql +53 -0
  115. package/vendor/serverd/src/lib/migrations/state/0017_membership_kind.sql +15 -0
  116. package/vendor/serverd/src/lib/migrations/state/0018_domain_group_keys.sql +23 -0
  117. package/vendor/serverd/src/lib/nginx.js +318 -140
  118. package/vendor/serverd/src/lib/owned-groups.js +326 -0
  119. package/vendor/serverd/src/lib/plugin-domain.js +86 -0
  120. package/vendor/serverd/src/lib/plugin-lifecycle.js +1 -0
  121. package/vendor/serverd/src/lib/plugin-oidc.js +144 -42
  122. package/vendor/serverd/src/lib/plugins.js +45 -5
  123. package/vendor/serverd/src/lib/registry-credential.js +39 -6
  124. package/vendor/serverd/src/lib/services.js +9 -1
  125. package/vendor/serverd/src/lib/state.js +11 -0
  126. package/vendor/serverd/src/lib/tunnel-deps.js +2 -4
  127. package/vendor/serverd/src/lib/user-surface.js +29 -4
  128. package/vendor/serverd/src/lib/users-lock.js +23 -0
  129. package/vendor/serverd/src/lib/vhost-reconcile.js +149 -41
  130. package/vendor/serverd/src/lib/web-surface.js +49 -32
  131. package/vendor/serverd/src/middleware/user-access-session.js +11 -4
  132. package/vendor/serverd/src/routes/invite.js +15 -5
  133. package/vendor/serverd/src/routes/management/domains.js +154 -0
  134. package/vendor/serverd/src/routes/management/gatekeeper-proxy.js +44 -8
  135. package/vendor/serverd/src/routes/management/identity.js +21 -7
  136. package/vendor/serverd/src/routes/management/invitations.js +33 -4
  137. package/vendor/serverd/src/routes/management/plugins.js +4 -2
  138. package/vendor/serverd/src/routes/management/registry-credential.js +86 -57
  139. package/vendor/serverd/src/routes/management/sites.js +50 -11
  140. package/vendor/serverd/src/routes/management/tunnels.js +29 -5
  141. package/vendor/serverd/src/routes/management/users.js +184 -64
  142. package/vendor/serverd/src/routes/management.js +2 -0
  143. package/vendor/serverd/src/routes/onboarding/dns.js +3 -17
  144. package/vendor/serverd/src/routes/onboarding/provision.js +22 -13
  145. package/vendor/serverd/src/routes/user-access.js +13 -1
  146. package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0001_groups.sql +0 -7
  147. package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0002_access_grants.sql +0 -14
  148. package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0003_access_request_log.sql +0 -9
  149. package/vendor/server-ui/dist/assets/index-BvEfCZYS.js +0 -84
@@ -0,0 +1,92 @@
1
+ /**
2
+ * The relay's domains as `lamaste-server domains` prints them — pure
3
+ * helpers, exported for tests (the command is src/commands/domains.js).
4
+ */
5
+
6
+ import { terminalText } from './plugin-settings.js';
7
+
8
+ /**
9
+ * One row per domain: name, state, members, what uses it — as terminal text.
10
+ *
11
+ * @param {Array<Record<string, any>>} domains as `GET /api/domains` lists them
12
+ */
13
+ export function domainRows(domains) {
14
+ return domains.map((d) => ({
15
+ name: terminalText(d.name),
16
+ state: d.status === 'active' ? (d.primary ? 'primary' : 'active') : 'pending',
17
+ members: Number.isInteger(d.members) ? d.members : 0,
18
+ externalMembers: Number.isInteger(d.externalMembers) ? d.externalMembers : 0,
19
+ usedBy: usageText(d.usedBy),
20
+ }));
21
+ }
22
+
23
+ /** What uses a domain, in one line (empty when nothing does). */
24
+ export function usageText(usedBy) {
25
+ if (!usedBy) return '';
26
+ const parts = [];
27
+ const count = (label, items) => {
28
+ if (Array.isArray(items) && items.length > 0) parts.push(`${items.length} ${label}`);
29
+ };
30
+ count('tunnel(s)', usedBy.tunnels);
31
+ count('site(s)', usedBy.sites);
32
+ count('plugin(s)', usedBy.plugins);
33
+ if (usedBy.registry) parts.push('the registry credential');
34
+ return parts.join(', ');
35
+ }
36
+
37
+ /** `/api/domains/<name><rest>`. */
38
+ export function domainPath(name, rest = '') {
39
+ return `/api/domains/${encodeURIComponent(name)}${rest}`;
40
+ }
41
+
42
+ /**
43
+ * A user's memberships in one line — `a.example, b.example (external)` —
44
+ * as terminal text; `—` for none. A bare name (an older panel's answer) is
45
+ * a member.
46
+ *
47
+ * @param {ReadonlyArray<string | { name: string, kind?: string }> | undefined} domains
48
+ */
49
+ export function membershipsText(domains) {
50
+ const parts = (domains ?? []).flatMap((d) => {
51
+ const name = typeof d === 'string' ? d : d?.name;
52
+ if (typeof name !== 'string') return [];
53
+ return [terminalText(d?.kind === 'external' ? `${name} (external)` : name)];
54
+ });
55
+ return parts.length > 0 ? parts.join(', ') : '—';
56
+ }
57
+
58
+ /** Whether any of `domains` is an external membership. */
59
+ export function isExternalAnywhere(domains) {
60
+ return (domains ?? []).some((d) => d?.kind === 'external');
61
+ }
62
+
63
+ /**
64
+ * What `domains members <domain> <action> <user> …` asks the panel, or
65
+ * null for a command line it does not take:
66
+ *
67
+ * - `add <user> [--external]` — a member (an external one with the flag);
68
+ * - `kind <user> member|external` — change the kind of a membership;
69
+ * - `remove <user>` — end it.
70
+ *
71
+ * `user` is a directory id or a username.
72
+ *
73
+ * @param {string} domain
74
+ * @param {string[]} rest what follows `members <domain>`
75
+ * @returns {{ method: 'PUT' | 'DELETE', path: string, body: object | null, user: string, kind: 'member' | 'external' | null } | null}
76
+ */
77
+ export function memberRequest(domain, rest) {
78
+ const [action, user, ...more] = rest;
79
+ if (!user) return null;
80
+ const path = domainPath(domain, `/members/${encodeURIComponent(user)}`);
81
+ if (action === 'remove' && more.length === 0) {
82
+ return { method: 'DELETE', path, body: null, user, kind: null };
83
+ }
84
+ if (action === 'add' && more.every((a) => a === '--external')) {
85
+ const kind = more.length > 0 ? 'external' : 'member';
86
+ return { method: 'PUT', path, body: { kind }, user, kind };
87
+ }
88
+ if (action === 'kind' && more.length === 1 && (more[0] === 'member' || more[0] === 'external')) {
89
+ return { method: 'PUT', path, body: { kind: more[0] }, user, kind: more[0] };
90
+ }
91
+ return null;
92
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Exit a command with a usage error or a panel refusal — as text, or as an
3
+ * NDJSON error carrying the refusal's `code` (e.g. `last_admin`,
4
+ * `domain_in_use`). The message is printed only as terminal text.
5
+ */
6
+
7
+ import chalk from 'chalk';
8
+ import { emitError } from './ndjson.js';
9
+ import { terminalText } from './plugin-settings.js';
10
+
11
+ /**
12
+ * @param {string} message
13
+ * @param {boolean} json
14
+ * @param {{ code?: string }} [err]
15
+ * @returns {never}
16
+ */
17
+ export function fail(message, json, err = {}) {
18
+ if (json) emitError(message, false, typeof err.code === 'string' ? { code: err.code } : {});
19
+ else console.error(`\n ${chalk.red(terminalText(message))}\n`);
20
+ process.exit(1);
21
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Grants on a plugin's user-facing OIDC clients as `lamaste-server plugins
3
+ * oidc-clients` takes and prints them — pure helpers, exported for tests
4
+ * (the command is src/commands/plugins.js).
5
+ *
6
+ * A grant is a Gatekeeper grant with `resourceType: 'oidc-client'` and the
7
+ * client id as `resourceId`; members of the plugin's domain sign in without
8
+ * one, an external member only with one (to them or a Lamaste group they
9
+ * belong to).
10
+ */
11
+
12
+ import { terminalText } from './plugin-settings.js';
13
+
14
+ /** The Gatekeeper resource type of a grant on an OIDC client. */
15
+ export const OIDC_CLIENT_RESOURCE = 'oidc-client';
16
+
17
+ /**
18
+ * The principal `--user <username>` or `--group <name>` names — exactly one
19
+ * of them — or null.
20
+ *
21
+ * @param {string[]} args
22
+ * @returns {{ principalType: 'user' | 'group', principalId: string } | null}
23
+ */
24
+ export function parsePrincipal(args) {
25
+ if (args.length !== 2) return null;
26
+ const [flag, id] = args;
27
+ if (!id || id.startsWith('--')) return null;
28
+ if (flag === '--user') return { principalType: 'user', principalId: id };
29
+ if (flag === '--group') return { principalType: 'group', principalId: id };
30
+ return null;
31
+ }
32
+
33
+ /** A grant's principal in words: the username, or `group:<name>`. */
34
+ export function principalText(grant) {
35
+ return terminalText(
36
+ grant.principalType === 'group' ? `group:${grant.principalId}` : grant.principalId,
37
+ );
38
+ }
39
+
40
+ /**
41
+ * Every grant on `clientId` that names `principal`, among the clients
42
+ * `GET /api/plugins/:name/oidc-clients` lists — Gatekeeper may hold more
43
+ * than one (grants differing only in their context), and revoking means
44
+ * revoking them all — or why there is none to revoke: `no_client` (not a
45
+ * user-facing client of the plugin), `unreadable` (Gatekeeper could not be
46
+ * read), `no_grant`.
47
+ *
48
+ * @param {ReadonlyArray<Record<string, any>>} clients
49
+ * @param {string} clientId
50
+ * @param {{ principalType: string, principalId: string }} principal
51
+ * @returns {{ grantIds: string[] } | { error: 'no_client' | 'unreadable' | 'no_grant' }}
52
+ */
53
+ export function findClientGrants(clients, clientId, principal) {
54
+ const client = clients.find((c) => c.id === clientId && c.userFacing);
55
+ if (!client) return { error: 'no_client' };
56
+ if (!Array.isArray(client.grants)) return { error: 'unreadable' };
57
+ const grantIds = client.grants
58
+ .filter(
59
+ (g) => g.principalType === principal.principalType && g.principalId === principal.principalId,
60
+ )
61
+ .map((g) => g.grantId);
62
+ return grantIds.length > 0 ? { grantIds } : { error: 'no_grant' };
63
+ }
@@ -129,6 +129,20 @@ export async function withCurlConfig(content, fn) {
129
129
  * @param {{ identity: { certPath: string, address: string, port: number }, method: string, path: string, body?: Record<string, unknown> | null, p12Path: string, configPath: string }} request
130
130
  * @returns {{ args: string[], input: string | undefined }}
131
131
  */
132
+ /**
133
+ * Refuse a request path with a `.` or `..` segment: curl and `fetch`
134
+ * resolve them (`/api/domains/../activate` → `/api/activate`), so a value
135
+ * put into a path — a domain, a user, a plugin — could name another route.
136
+ * Every path the client sends passes here; values are percent-encoded as
137
+ * usual where the path is built.
138
+ */
139
+ export function assertPanelPath(path) {
140
+ const route = String(path).split(/[?#]/, 1)[0];
141
+ if (route.split('/').some((segment) => segment === '.' || segment === '..')) {
142
+ throw new Error('A panel request path may not have a "." or ".." segment');
143
+ }
144
+ }
145
+
132
146
  export function buildPanelCurlRequest({
133
147
  identity,
134
148
  method,
@@ -137,6 +151,7 @@ export function buildPanelCurlRequest({
137
151
  p12Path,
138
152
  configPath,
139
153
  }) {
154
+ assertPanelPath(path);
140
155
  const { certPath, address, port } = identity;
141
156
  const args = [
142
157
  '-q', // No .curlrc — it must be the first argument