@lamalibre/create-lamaste 4.4.0 → 4.5.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/package.json +2 -2
- package/scripts/lamaste-priv +200 -16
- package/src/index.js +1 -0
- package/src/lib/backend-cookie.js +65 -0
- package/src/lib/ip-panel-vhost.js +208 -0
- package/src/lib/service-config.js +5 -3
- package/src/tasks/nginx.js +18 -135
- package/src/tasks/redeploy.js +39 -1
- package/vendor/docs/00-introduction/how-it-works.md +2 -2
- package/vendor/docs/00-introduction/quickstart.md +5 -5
- package/vendor/docs/00-introduction/what-is-lamaste.md +5 -5
- package/vendor/docs/01-concepts/authentication.md +1 -1
- package/vendor/docs/01-concepts/authorization.md +66 -48
- package/vendor/docs/01-concepts/certificates.md +3 -3
- package/vendor/docs/01-concepts/mtls.md +2 -2
- package/vendor/docs/01-concepts/security-model.md +70 -33
- package/vendor/docs/01-concepts/tickets.md +2 -2
- package/vendor/docs/01-concepts/tunneling.md +4 -4
- package/vendor/docs/02-guides/agent-setup.md +5 -5
- package/vendor/docs/02-guides/certificate-management.md +2 -2
- package/vendor/docs/02-guides/cloud-provisioning.md +3 -3
- package/vendor/docs/02-guides/desktop-app-setup.md +10 -10
- package/vendor/docs/02-guides/installation.md +3 -3
- package/vendor/docs/02-guides/managing-users.md +113 -25
- package/vendor/docs/02-guides/onboarding.md +2 -2
- package/vendor/docs/02-guides/standalone-plugin-with-tickets.md +2 -2
- package/vendor/docs/03-architecture/installer-flow.md +2 -2
- package/vendor/docs/03-architecture/installer.md +4 -4
- package/vendor/docs/03-architecture/management-flow.md +3 -1
- package/vendor/docs/03-architecture/nginx-configuration.md +165 -18
- package/vendor/docs/03-architecture/overview.md +13 -17
- package/vendor/docs/03-architecture/panel-client.md +6 -6
- package/vendor/docs/03-architecture/panel-server.md +3 -2
- package/vendor/docs/03-architecture/state-management.md +4 -8
- package/vendor/docs/03-architecture/system-overview.md +1 -1
- package/vendor/docs/04-api-reference/agent-hosted-plugins.md +292 -23
- package/vendor/docs/04-api-reference/app-host.md +3 -3
- package/vendor/docs/04-api-reference/gatekeeper.md +60 -5
- package/vendor/docs/04-api-reference/lifecycle-hooks.md +15 -13
- package/vendor/docs/04-api-reference/onboarding.md +1 -1
- package/vendor/docs/04-api-reference/registry-and-oidc.md +6 -4
- package/vendor/docs/04-api-reference/tunnels.md +67 -29
- package/vendor/docs/04-api-reference/users.md +241 -45
- package/vendor/docs/05-operations/backup-and-restore.md +35 -26
- package/vendor/docs/05-operations/monitoring.md +39 -0
- package/vendor/docs/05-operations/uninstalling.md +2 -1
- package/vendor/docs/05-operations/upgrades.md +15 -6
- package/vendor/docs/06-reference/config-files.md +83 -189
- package/vendor/docs/06-reference/glossary.md +3 -3
- package/vendor/docs/06-reference/installer-flags.md +13 -13
- package/vendor/docs/06-reference/ports-and-services.md +25 -26
- package/vendor/docs/06-reference/troubleshooting.md +8 -8
- package/vendor/docs/gatekeeper-design.md +10 -8
- package/vendor/docs/identity-directory-design.md +48 -14
- package/vendor/docs/package.json +1 -1
- package/vendor/docs/todo/lib-cli-desktop-refactor.md +27 -27
- package/vendor/gatekeeper/dist/index.d.ts +2 -1
- package/vendor/gatekeeper/dist/index.d.ts.map +1 -1
- package/vendor/gatekeeper/dist/index.js +2 -0
- package/vendor/gatekeeper/dist/index.js.map +1 -1
- package/vendor/gatekeeper/dist/lib/grants.d.ts +6 -0
- package/vendor/gatekeeper/dist/lib/grants.d.ts.map +1 -1
- package/vendor/gatekeeper/dist/lib/grants.js +6 -1
- package/vendor/gatekeeper/dist/lib/grants.js.map +1 -1
- package/vendor/gatekeeper/dist/lib/groups.d.ts +5 -0
- package/vendor/gatekeeper/dist/lib/groups.d.ts.map +1 -1
- package/vendor/gatekeeper/dist/lib/groups.js +5 -1
- package/vendor/gatekeeper/dist/lib/groups.js.map +1 -1
- package/vendor/gatekeeper/dist/lib/principals.d.ts +29 -0
- package/vendor/gatekeeper/dist/lib/principals.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/principals.js +67 -0
- package/vendor/gatekeeper/dist/lib/principals.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/tunnels.d.ts +13 -0
- package/vendor/gatekeeper/dist/lib/tunnels.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/tunnels.js +68 -0
- package/vendor/gatekeeper/dist/lib/tunnels.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/types.d.ts +19 -0
- package/vendor/gatekeeper/dist/lib/types.d.ts.map +1 -1
- package/vendor/gatekeeper/dist/server/index.d.ts.map +1 -1
- package/vendor/gatekeeper/dist/server/index.js +22 -48
- package/vendor/gatekeeper/dist/server/index.js.map +1 -1
- package/vendor/gatekeeper/dist/server/routes/authz.d.ts +15 -1
- package/vendor/gatekeeper/dist/server/routes/authz.d.ts.map +1 -1
- package/vendor/gatekeeper/dist/server/routes/authz.js +250 -103
- package/vendor/gatekeeper/dist/server/routes/authz.js.map +1 -1
- package/vendor/gatekeeper/dist/server/routes/diagnostic.d.ts.map +1 -1
- package/vendor/gatekeeper/dist/server/routes/diagnostic.js +61 -4
- package/vendor/gatekeeper/dist/server/routes/diagnostic.js.map +1 -1
- package/vendor/gatekeeper/dist/server/routes/principals.d.ts +3 -0
- package/vendor/gatekeeper/dist/server/routes/principals.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/principals.js +27 -0
- package/vendor/gatekeeper/dist/server/routes/principals.js.map +1 -0
- package/vendor/gatekeeper/package.json +2 -2
- package/vendor/server/bin/lamaste-server.js +15 -1
- package/vendor/server/package.json +2 -2
- package/vendor/server/src/commands/plugin-identity.js +335 -0
- package/vendor/server/src/commands/plugins.js +99 -11
- package/vendor/server/src/commands/registry-credential.js +2 -1
- package/vendor/server/src/commands/tunnels.js +102 -2
- package/vendor/server/src/commands/uninstall.js +3 -1
- package/vendor/server/src/commands/users.js +358 -0
- package/vendor/server/src/plugin-approval.js +107 -5
- package/vendor/server/src/plugin-identity.js +382 -0
- package/vendor/server-ui/dist/assets/index-D-fer5ef.css +1 -0
- package/vendor/server-ui/dist/assets/index-DSaDPJaw.js +84 -0
- package/vendor/server-ui/dist/index.html +2 -2
- package/vendor/serverd/package.json +2 -2
- package/vendor/serverd/src/index.js +25 -1
- package/vendor/serverd/src/lib/audience.js +576 -0
- package/vendor/serverd/src/lib/authelia.js +78 -7
- package/vendor/serverd/src/lib/config.js +68 -43
- package/vendor/serverd/src/lib/directory.js +1113 -0
- package/vendor/serverd/src/lib/entitlements.js +517 -0
- package/vendor/serverd/src/lib/gatekeeper-client.js +215 -10
- package/vendor/serverd/src/lib/hook-pipelines.js +13 -0
- package/vendor/serverd/src/lib/identity-sync.js +905 -0
- package/vendor/serverd/src/lib/migrations/state/0013_identity_directory.sql +158 -0
- package/vendor/serverd/src/lib/migrations/state/0014_identity_ledger.sql +20 -0
- package/vendor/serverd/src/lib/mtls.js +6 -2
- package/vendor/serverd/src/lib/nginx.js +279 -48
- package/vendor/serverd/src/lib/plugin-hooks.js +98 -45
- package/vendor/serverd/src/lib/plugin-hosting.js +33 -5
- package/vendor/serverd/src/lib/plugin-lifecycle.js +100 -9
- package/vendor/serverd/src/lib/plugin-packages.js +30 -22
- package/vendor/serverd/src/lib/plugins.js +94 -15
- package/vendor/serverd/src/lib/registry-credential.js +46 -6
- package/vendor/serverd/src/lib/request-logging.js +19 -0
- package/vendor/serverd/src/lib/retained-data.js +50 -4
- package/vendor/serverd/src/lib/state.js +18 -0
- package/vendor/serverd/src/lib/tunnel-hooks.js +4 -1
- package/vendor/serverd/src/lib/user-access-session.js +21 -11
- package/vendor/serverd/src/lib/user-caller.js +67 -0
- package/vendor/serverd/src/lib/user-surface.js +733 -0
- package/vendor/serverd/src/lib/vhost-reconcile.js +136 -34
- package/vendor/serverd/src/middleware/user-access-session.js +9 -0
- package/vendor/serverd/src/routes/agent-forwarder.js +4 -2
- package/vendor/serverd/src/routes/invite.js +53 -12
- package/vendor/serverd/src/routes/management/gatekeeper-proxy.js +107 -16
- package/vendor/serverd/src/routes/management/identity-directory.js +79 -0
- package/vendor/serverd/src/routes/management/invitations.js +78 -68
- package/vendor/serverd/src/routes/management/plugin-identity.js +221 -0
- package/vendor/serverd/src/routes/management/registry-credential.js +1 -1
- package/vendor/serverd/src/routes/management/tunnels.js +98 -2
- package/vendor/serverd/src/routes/management/users.js +290 -208
- package/vendor/serverd/src/routes/management.js +4 -0
- package/vendor/serverd/src/routes/onboarding/provision.js +14 -1
- package/vendor/serverd/src/routes/user-access.js +17 -4
- package/vendor/serverd/src/routes/user-surface.js +69 -0
- package/vendor/server-ui/dist/assets/index-25wo_8av.css +0 -1
- package/vendor/server-ui/dist/assets/index-CESyyK8V.js +0 -84
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
* Tiny gatekeeper admin-API client for callers other than the
|
|
3
3
|
* /api/gatekeeper/* proxy routes.
|
|
4
4
|
*
|
|
5
|
-
* Reads the shared secret on first use (cached for the process lifetime
|
|
6
|
-
* and forwards a request to http://127.0.0.1:9294/api/...
|
|
5
|
+
* Reads the shared secret on first use (cached for the process lifetime
|
|
6
|
+
* once found) and forwards a request to http://127.0.0.1:9294/api/...
|
|
7
7
|
*
|
|
8
8
|
* Used by the user-delete route to cascade-revoke grants belonging to a
|
|
9
|
-
* deleted Authelia user,
|
|
10
|
-
*
|
|
9
|
+
* deleted Authelia user, by the plugin audiences (grants and groups), the
|
|
10
|
+
* entitlements and the erasure of a removed user, and by other internal
|
|
11
|
+
* callers that need to talk to gatekeeper without going through the
|
|
12
|
+
* panel's own mTLS layer.
|
|
11
13
|
*/
|
|
12
14
|
|
|
13
15
|
import { readFile } from 'node:fs/promises';
|
|
@@ -16,17 +18,31 @@ import { getConfigPath } from './config.js';
|
|
|
16
18
|
|
|
17
19
|
const GATEKEEPER_BASE = 'http://127.0.0.1:9294';
|
|
18
20
|
let cachedSecret = null;
|
|
19
|
-
|
|
21
|
+
|
|
22
|
+
/** Replaces the HTTP exchange with Gatekeeper (tests: there is no Gatekeeper). */
|
|
23
|
+
let transportOverride = null;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Answer every Gatekeeper request with `transport(method, apiPath, body)`
|
|
27
|
+
* — a {@link GatekeeperResult} — instead of calling Gatekeeper (tests);
|
|
28
|
+
* `null` calls Gatekeeper again.
|
|
29
|
+
*
|
|
30
|
+
* @param {((method: string, apiPath: string, body?: unknown) => Promise<GatekeeperResult>) | null} transport
|
|
31
|
+
*/
|
|
32
|
+
export function setGatekeeperClientForTests(transport) {
|
|
33
|
+
transportOverride = transport;
|
|
34
|
+
}
|
|
20
35
|
|
|
21
36
|
/**
|
|
22
37
|
* Load the gatekeeper API secret. Supports both the new JSON envelope
|
|
23
38
|
* (`gatekeeper-secret.json`) written by the gatekeeper service and the
|
|
24
39
|
* legacy plain-text file (`gatekeeper-secret`). Returns null if neither
|
|
25
|
-
* exists — callers must treat this as "gatekeeper not installed".
|
|
40
|
+
* exists — callers must treat this as "gatekeeper not installed (yet)".
|
|
41
|
+
* Only a secret that was found is kept: Gatekeeper writes it when it first
|
|
42
|
+
* starts, which may be after the panel's first call.
|
|
26
43
|
*/
|
|
27
44
|
async function loadSecret() {
|
|
28
|
-
if (
|
|
29
|
-
secretLoaded = true;
|
|
45
|
+
if (cachedSecret) return cachedSecret;
|
|
30
46
|
|
|
31
47
|
const configDir = path.dirname(getConfigPath());
|
|
32
48
|
|
|
@@ -53,8 +69,7 @@ async function loadSecret() {
|
|
|
53
69
|
// No secret available
|
|
54
70
|
}
|
|
55
71
|
|
|
56
|
-
|
|
57
|
-
return cachedSecret;
|
|
72
|
+
return null;
|
|
58
73
|
}
|
|
59
74
|
|
|
60
75
|
/**
|
|
@@ -78,6 +93,8 @@ async function loadSecret() {
|
|
|
78
93
|
* @returns {Promise<GatekeeperResult>}
|
|
79
94
|
*/
|
|
80
95
|
export async function gatekeeperRequest(method, apiPath, body) {
|
|
96
|
+
if (transportOverride) return transportOverride(method, apiPath, body);
|
|
97
|
+
|
|
81
98
|
const secret = await loadSecret();
|
|
82
99
|
if (!secret) {
|
|
83
100
|
return {
|
|
@@ -133,3 +150,191 @@ export async function gatekeeperRequest(method, apiPath, body) {
|
|
|
133
150
|
|
|
134
151
|
return { ok: true, statusCode: response.status, data };
|
|
135
152
|
}
|
|
153
|
+
|
|
154
|
+
// ---------------------------------------------------------------------------
|
|
155
|
+
// Typed calls
|
|
156
|
+
// ---------------------------------------------------------------------------
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* A grant as Gatekeeper lists it.
|
|
160
|
+
*
|
|
161
|
+
* @typedef {object} GatekeeperGrant
|
|
162
|
+
* @property {string} grantId
|
|
163
|
+
* @property {'user' | 'group'} principalType
|
|
164
|
+
* @property {string} principalId
|
|
165
|
+
* @property {string} resourceType
|
|
166
|
+
* @property {string} resourceId
|
|
167
|
+
* @property {Record<string, unknown>} context
|
|
168
|
+
* @property {boolean} used
|
|
169
|
+
* @property {string} createdAt
|
|
170
|
+
*/
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* A Lamaste group as Gatekeeper lists it.
|
|
174
|
+
*
|
|
175
|
+
* @typedef {object} GatekeeperGroup
|
|
176
|
+
* @property {string} name
|
|
177
|
+
* @property {string} description
|
|
178
|
+
* @property {string[]} members usernames
|
|
179
|
+
*/
|
|
180
|
+
|
|
181
|
+
function isRecord(value) {
|
|
182
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** A failed call as a result, for an answer that is not the shape Gatekeeper gives. */
|
|
186
|
+
function malformed(what) {
|
|
187
|
+
return {
|
|
188
|
+
ok: false,
|
|
189
|
+
statusCode: 502,
|
|
190
|
+
error: `gatekeeper answered ${what} in an unexpected shape`,
|
|
191
|
+
reason: 'http-error',
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function grantOf(raw) {
|
|
196
|
+
if (
|
|
197
|
+
!isRecord(raw) ||
|
|
198
|
+
typeof raw.grantId !== 'string' ||
|
|
199
|
+
(raw.principalType !== 'user' && raw.principalType !== 'group') ||
|
|
200
|
+
typeof raw.principalId !== 'string' ||
|
|
201
|
+
typeof raw.resourceType !== 'string' ||
|
|
202
|
+
typeof raw.resourceId !== 'string'
|
|
203
|
+
) {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
return {
|
|
207
|
+
grantId: raw.grantId,
|
|
208
|
+
principalType: raw.principalType,
|
|
209
|
+
principalId: raw.principalId,
|
|
210
|
+
resourceType: raw.resourceType,
|
|
211
|
+
resourceId: raw.resourceId,
|
|
212
|
+
context: isRecord(raw.context) ? raw.context : {},
|
|
213
|
+
used: raw.used === true,
|
|
214
|
+
createdAt: typeof raw.createdAt === 'string' ? raw.createdAt : '',
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function groupOf(raw) {
|
|
219
|
+
if (!isRecord(raw) || typeof raw.name !== 'string' || !Array.isArray(raw.members)) return null;
|
|
220
|
+
return {
|
|
221
|
+
name: raw.name,
|
|
222
|
+
description: typeof raw.description === 'string' ? raw.description : '',
|
|
223
|
+
members: raw.members.filter((m) => typeof m === 'string'),
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Every grant on plugin `route` (`resourceType: 'plugin'`, `resourceId:
|
|
229
|
+
* <route>`) — with roles or without.
|
|
230
|
+
*
|
|
231
|
+
* @param {string} route
|
|
232
|
+
* @returns {Promise<{ ok: true, grants: GatekeeperGrant[] } | Extract<GatekeeperResult, { ok: false }>>}
|
|
233
|
+
*/
|
|
234
|
+
export async function listPluginGrants(route) {
|
|
235
|
+
const qs = new URLSearchParams({ resourceType: 'plugin', resourceId: route }).toString();
|
|
236
|
+
const result = await gatekeeperRequest('GET', `/api/grants?${qs}`);
|
|
237
|
+
if (!result.ok) return result;
|
|
238
|
+
if (!isRecord(result.data) || !Array.isArray(result.data.grants)) return malformed('its grants');
|
|
239
|
+
const grants = result.data.grants.map(grantOf);
|
|
240
|
+
if (grants.some((g) => g === null)) return malformed('its grants');
|
|
241
|
+
// Gatekeeper filters by the query; a grant on anything else is not this plugin's.
|
|
242
|
+
return {
|
|
243
|
+
ok: true,
|
|
244
|
+
grants: grants.filter((g) => g.resourceType === 'plugin' && g.resourceId === route),
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Every Lamaste group with its members.
|
|
250
|
+
*
|
|
251
|
+
* @returns {Promise<{ ok: true, groups: GatekeeperGroup[] } | Extract<GatekeeperResult, { ok: false }>>}
|
|
252
|
+
*/
|
|
253
|
+
export async function listGatekeeperGroups() {
|
|
254
|
+
const result = await gatekeeperRequest('GET', '/api/groups');
|
|
255
|
+
if (!result.ok) return result;
|
|
256
|
+
if (!isRecord(result.data) || !Array.isArray(result.data.groups)) return malformed('its groups');
|
|
257
|
+
const groups = result.data.groups.map(groupOf);
|
|
258
|
+
if (groups.some((g) => g === null)) return malformed('its groups');
|
|
259
|
+
return { ok: true, groups };
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Create a grant. Gatekeeper refuses a duplicate (409: same principal,
|
|
264
|
+
* resource and context JSON) and a grant beyond its limit (503).
|
|
265
|
+
*
|
|
266
|
+
* @param {{ principalType: 'user' | 'group', principalId: string, resourceType: string, resourceId: string, context: Record<string, unknown> }} grant
|
|
267
|
+
* @returns {Promise<{ ok: true, grant: GatekeeperGrant } | Extract<GatekeeperResult, { ok: false }>>}
|
|
268
|
+
*/
|
|
269
|
+
export async function createGatekeeperGrant(grant) {
|
|
270
|
+
const result = await gatekeeperRequest('POST', '/api/grants', grant);
|
|
271
|
+
if (!result.ok) return result;
|
|
272
|
+
const created = isRecord(result.data) ? grantOf(result.data.grant) : null;
|
|
273
|
+
return created ? { ok: true, grant: created } : malformed('the grant it created');
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Revoke one grant.
|
|
278
|
+
*
|
|
279
|
+
* @param {string} grantId
|
|
280
|
+
* @returns {Promise<GatekeeperResult>}
|
|
281
|
+
*/
|
|
282
|
+
export function revokeGatekeeperGrant(grantId) {
|
|
283
|
+
return gatekeeperRequest('DELETE', `/api/grants/${encodeURIComponent(grantId)}`);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Remove everything Gatekeeper holds under `username` — every grant naming
|
|
288
|
+
* the user (any resource type) and its membership of every group — in one
|
|
289
|
+
* transaction. Idempotent. The username goes in the body (a URL is what
|
|
290
|
+
* request logs keep).
|
|
291
|
+
*
|
|
292
|
+
* @param {string} username
|
|
293
|
+
* @returns {Promise<{ ok: true, grantsRemoved: number, groupsLeft: number } | Extract<GatekeeperResult, { ok: false }>>}
|
|
294
|
+
*/
|
|
295
|
+
export async function removeUserPrincipal(username) {
|
|
296
|
+
const result = await gatekeeperRequest('POST', '/api/principals/user/remove', { username });
|
|
297
|
+
if (!result.ok) return result;
|
|
298
|
+
const data = isRecord(result.data) ? result.data : {};
|
|
299
|
+
if (
|
|
300
|
+
!Number.isSafeInteger(data.grantsRemoved) ||
|
|
301
|
+
data.grantsRemoved < 0 ||
|
|
302
|
+
!Number.isSafeInteger(data.groupsLeft) ||
|
|
303
|
+
data.groupsLeft < 0
|
|
304
|
+
) {
|
|
305
|
+
return malformed('the principal removal');
|
|
306
|
+
}
|
|
307
|
+
return { ok: true, grantsRemoved: data.grantsRemoved, groupsLeft: data.groupsLeft };
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Drop Gatekeeper's cached session verdicts. Authelia ends every session
|
|
312
|
+
* when users.yml changes; without this a cached verdict would keep a
|
|
313
|
+
* disabled or deleted user in for up to the cache lifetime (30 s).
|
|
314
|
+
*
|
|
315
|
+
* @returns {Promise<{ ok: true } | Extract<GatekeeperResult, { ok: false }>>}
|
|
316
|
+
*/
|
|
317
|
+
export async function bustGatekeeperSessions() {
|
|
318
|
+
const result = await gatekeeperRequest('POST', '/api/cache/bust');
|
|
319
|
+
return result.ok ? { ok: true } : result;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Delete the access-request log rows of `username` (logged before
|
|
324
|
+
* `before`, an ISO 8601 time, when given) — part of erasing a removed user.
|
|
325
|
+
* The username goes in the body (a URL is what request logs keep).
|
|
326
|
+
*
|
|
327
|
+
* @param {string} username
|
|
328
|
+
* @param {{ before?: string }} [opts]
|
|
329
|
+
* @returns {Promise<{ ok: true, removed: number } | Extract<GatekeeperResult, { ok: false }>>}
|
|
330
|
+
*/
|
|
331
|
+
export async function purgeAccessLogForUser(username, { before } = {}) {
|
|
332
|
+
const result = await gatekeeperRequest('POST', '/api/access-log/purge', {
|
|
333
|
+
username,
|
|
334
|
+
...(before ? { before } : {}),
|
|
335
|
+
});
|
|
336
|
+
if (!result.ok) return result;
|
|
337
|
+
const removed = isRecord(result.data) ? result.data.removed : undefined;
|
|
338
|
+
if (!Number.isSafeInteger(removed) || removed < 0) return malformed('the purge');
|
|
339
|
+
return { ok: true, removed };
|
|
340
|
+
}
|
|
@@ -83,6 +83,8 @@ import { forgetRetainedData, recordRetainedData } from './retained-data.js';
|
|
|
83
83
|
import { runRelayHook } from './relay-hooks.js';
|
|
84
84
|
import { recordAuditBestEffort } from './audit.js';
|
|
85
85
|
import { deleteSettingsCache, relayHookSettings } from './plugin-settings.js';
|
|
86
|
+
import { forgetIdentityHalf } from './identity-sync.js';
|
|
87
|
+
import { refreshUserSurfaceVhosts } from './user-surface.js';
|
|
86
88
|
import {
|
|
87
89
|
tunnelNginxDeps,
|
|
88
90
|
tunnelCertbotDeps,
|
|
@@ -620,6 +622,9 @@ export async function runPurgeRetainedData(op, { retained, logger }) {
|
|
|
620
622
|
});
|
|
621
623
|
assertRemoved(result, 'Purging the retained data');
|
|
622
624
|
await forgetRetainedData(retained.agentLabel, retained.route);
|
|
625
|
+
// The last data of an uninstalled plugin: its identity ledger goes too.
|
|
626
|
+
const { dropIdentityLedgerIfNoData } = await import('./audience.js');
|
|
627
|
+
await dropIdentityLedgerIfNoData(retained.plugin, logger);
|
|
623
628
|
});
|
|
624
629
|
await recordAuditBestEffort(
|
|
625
630
|
{
|
|
@@ -836,6 +841,14 @@ export async function runUninstallPipeline(op, ctx, { force = false } = {}) {
|
|
|
836
841
|
await deleteSettingsCache(ctx.plugin.name).catch((err) =>
|
|
837
842
|
op.warn(`Could not drop the cached settings: ${err.message}`),
|
|
838
843
|
);
|
|
844
|
+
// So does what the relay knew of the half's identity feed: the next
|
|
845
|
+
// hosting's half answers protocol 1 before users reach it, and replays.
|
|
846
|
+
await forgetIdentityHalf(ctx.plugin.name).catch((err) =>
|
|
847
|
+
op.warn(`Could not forget the agent half's identity feed: ${err.message}`),
|
|
848
|
+
);
|
|
849
|
+
// Its user surface's locations no longer take the internal tunnel's
|
|
850
|
+
// body limit (best effort; the panel answers 503 until it is hosted).
|
|
851
|
+
await refreshUserSurfaceVhosts(ctx.route, ctx.logger);
|
|
839
852
|
}
|
|
840
853
|
await recordAuditBestEffort(
|
|
841
854
|
{
|