@jwilleke/ngdpbase-types 5.25.0 → 5.27.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/README.md +1 -1
- package/lib/packages/ngdpbase-types/index.d.ts +9 -2
- package/lib/packages/ngdpbase-types/testing.d.ts +7 -3
- package/lib/packages/ngdpbase-types/testing.js +7 -3
- package/lib/src/converters/DocxConverter.d.ts +30 -0
- package/lib/src/converters/MarkdownConverter.d.ts +31 -0
- package/lib/src/export/ChromeServicePdfProvider.d.ts +52 -0
- package/lib/src/export/ChromeServicePdfProvider.js +153 -0
- package/lib/src/export/LocalChromePdfProvider.d.ts +25 -0
- package/lib/src/export/LocalChromePdfProvider.js +33 -0
- package/lib/src/export/PdfProvider.d.ts +51 -0
- package/lib/src/export/PdfProvider.js +37 -0
- package/lib/src/export/pdfEngine.d.ts +113 -0
- package/lib/src/export/pdfEngine.js +195 -0
- package/lib/src/http/egressPolicy.js +61 -0
- package/lib/src/http/guardedElasticsearch.d.ts +27 -0
- package/lib/src/http/guardedElasticsearch.js +99 -0
- package/lib/src/ice/converterShim.d.ts +5 -0
- package/lib/src/ice/fileHandling.d.ts +30 -0
- package/lib/src/ice/ledger.d.ts +37 -0
- package/lib/src/ice/runImport.d.ts +51 -0
- package/lib/src/ice/types.d.ts +262 -0
- package/lib/src/mail/MailProvider.d.ts +15 -0
- package/lib/src/mail/NodemailerMailProvider.d.ts +1 -1
- package/lib/src/managers/AddonsManager.d.ts +55 -3
- package/lib/src/managers/AttachmentManager.d.ts +4 -5
- package/lib/src/managers/AuthManager.d.ts +150 -32
- package/lib/src/managers/BackgroundJobManager.d.ts +22 -12
- package/lib/src/managers/BackupManager.d.ts +779 -0
- package/lib/src/managers/BackupManager.js +2042 -0
- package/lib/src/managers/BaseManager.d.ts +156 -6
- package/lib/src/managers/BaseManager.js +64 -2
- package/lib/src/managers/CommentManager.d.ts +6 -6
- package/lib/src/managers/ConsentManager.d.ts +130 -0
- package/lib/src/managers/DatabaseManager.d.ts +6 -0
- package/lib/src/managers/DatabaseManager.js +25 -15
- package/lib/src/managers/EmailManager.d.ts +32 -5
- package/lib/src/managers/ExportManager.d.ts +62 -0
- package/lib/src/managers/ExportManager.js +147 -2
- package/lib/src/managers/FootnoteManager.d.ts +6 -6
- package/lib/src/managers/ImportManager.d.ts +590 -0
- package/lib/src/managers/OrganizationManager.d.ts +8 -6
- package/lib/src/managers/PageManager.d.ts +61 -1
- package/lib/src/managers/PersonManager.d.ts +8 -6
- package/lib/src/managers/RoleManager.d.ts +6 -8
- package/lib/src/providers/BaseAttachmentProvider.d.ts +4 -4
- package/lib/src/providers/BaseBackupProvider.d.ts +77 -0
- package/lib/src/providers/BaseBackupProvider.js +52 -0
- package/lib/src/providers/BasicAttachmentProvider.d.ts +4 -4
- package/lib/src/providers/FileBackupProvider.d.ts +34 -0
- package/lib/src/providers/FileBackupProvider.js +116 -0
- package/lib/src/providers/FileConsentProvider.d.ts +18 -0
- package/lib/src/providers/SqliteDatabaseProvider.d.ts +2 -2
- package/lib/src/providers/SqliteDatabaseProvider.js +12 -11
- package/lib/src/providers/attachmentFilesBackup.d.ts +5 -4
- package/lib/src/providers/databaseMigrations.js +45 -0
- package/lib/src/security/PolicyDecisionPoint.d.ts +14 -0
- package/lib/src/security/PolicyInformationPoint.d.ts +25 -2
- package/lib/src/types/Config.d.ts +2 -0
- package/lib/src/types/Consent.d.ts +90 -0
- package/lib/src/types/express.d.ts +6 -0
- package/lib/src/utils/addonHost.d.ts +45 -2
- package/lib/src/utils/addonHost.js +39 -3
- package/lib/src/utils/auditEventNames.generated.d.ts +20 -0
- package/lib/src/utils/auditEventNames.generated.js +20 -0
- package/lib/src/utils/backupCrypto.js +275 -0
- package/lib/src/utils/backupDocument.d.ts +36 -0
- package/lib/src/utils/backupDocument.js +99 -0
- package/lib/src/utils/browserDescription.d.ts +11 -0
- package/lib/src/utils/directoryBackup.d.ts +48 -48
- package/lib/src/utils/directoryBackup.js +291 -0
- package/lib/src/utils/frontmatterAccess.d.ts +54 -11
- package/lib/src/utils/importRunSummary.d.ts +64 -0
- package/lib/src/utils/instanceEnvBackup.d.ts +46 -0
- package/lib/src/utils/instanceEnvBackup.js +120 -0
- package/lib/src/utils/instanceEnvSecret.js +79 -0
- package/lib/src/utils/ipLocation.d.ts +11 -0
- package/lib/src/utils/ncmNotify.d.ts +30 -0
- package/lib/src/utils/privateStoreImport.d.ts +58 -0
- package/lib/src/utils/schedule.js +171 -0
- package/lib/src/utils/secretFileMode.js +78 -0
- package/lib/src/utils/siteRestore.d.ts +13 -16
- package/lib/src/utils/siteRestore.js +33 -6
- package/lib/src/utils/treeArchive.d.ts +2 -0
- package/lib/src/utils/treeArchive.js +242 -0
- package/lib/src/utils/version.js +408 -0
- package/lib/src/utils/zipArchive.d.ts +71 -0
- package/package.json +6 -1
- package/lib/src/utils/administrators.d.ts +0 -21
|
@@ -146,6 +146,20 @@ export declare class PolicyDecisionPoint extends BaseManager {
|
|
|
146
146
|
* cannot be asked by accident. The subject is the PIP's, resolved live.
|
|
147
147
|
*/
|
|
148
148
|
userHoldsPermission(username: string, action: string): Promise<boolean>;
|
|
149
|
+
/**
|
|
150
|
+
* The usernames of every account that holds a permission, each asked through
|
|
151
|
+
* {@link userHoldsPermission} (#1813; was `utils/administrators.ts`). With
|
|
152
|
+
* `admin-system` this is "who are the administrators": the people an
|
|
153
|
+
* operator problem is reported to (#1431 7b, #1916), never a role name.
|
|
154
|
+
*
|
|
155
|
+
* An empty answer means "nobody to tell", and a caller sending a
|
|
156
|
+
* notification must then only log it: an empty `targetUsers` list means
|
|
157
|
+
* every user in NotificationManager.
|
|
158
|
+
*
|
|
159
|
+
* @returns The usernames, or null when there is no UserManager to list
|
|
160
|
+
* accounts from (tests, a half-booted engine); treat that like nobody
|
|
161
|
+
*/
|
|
162
|
+
usersHolding(action: string): Promise<string[] | null>;
|
|
149
163
|
/**
|
|
150
164
|
* The permissions a user's roles give them, for display (#1431 step 10).
|
|
151
165
|
*
|
|
@@ -209,6 +209,12 @@ declare class PolicyInformationPoint extends BaseManager {
|
|
|
209
209
|
* none when it is not running.
|
|
210
210
|
*/
|
|
211
211
|
subjectEnabledAddons(ctx: ActorContext): Promise<string[]>;
|
|
212
|
+
/**
|
|
213
|
+
* {@link subjectEnabledAddons} for a page decision (#1964): none for no
|
|
214
|
+
* subject, and none when the lookup fails — an `addon:<slug>` entry then
|
|
215
|
+
* matches nobody, never everyone.
|
|
216
|
+
*/
|
|
217
|
+
private enabledAddonsOf;
|
|
212
218
|
private userManager;
|
|
213
219
|
private roleManager;
|
|
214
220
|
/**
|
|
@@ -326,7 +332,9 @@ declare class PolicyInformationPoint extends BaseManager {
|
|
|
326
332
|
* - __Tier 0.5, author-lock__, `edit` only. It only denies: an author, or a
|
|
327
333
|
* subject holding `admin-system` (#1431 7b), falls through to Tier 1+.
|
|
328
334
|
* - __Tier 1, frontmatter__ `access[action]` / `audience`. Page-level, and
|
|
329
|
-
* overrides global policy when it states a rule (#1054).
|
|
335
|
+
* overrides global policy when it states a rule (#1054). An `audience`
|
|
336
|
+
* covers every action, and a subject holding `admin-system` is its one
|
|
337
|
+
* exception; `access` has none (#1964).
|
|
330
338
|
* - __Tier 2.__ For a share visitor the share IS the policy (#1222); for
|
|
331
339
|
* anyone else, global policy.
|
|
332
340
|
* - __Default deny.__ Nothing below Tier 2 grants — JSPWiki's page-body ACL
|
|
@@ -439,10 +447,25 @@ declare class PolicyInformationPoint extends BaseManager {
|
|
|
439
447
|
*/
|
|
440
448
|
canUserAccessMediaItem(userContext: UserContext | null | undefined, item: MediaItem): Promise<boolean>;
|
|
441
449
|
/**
|
|
442
|
-
* Check front matter audience / access fields (Tier 1
|
|
450
|
+
* Check front matter audience / access fields (Tier 1).
|
|
443
451
|
* Returns a decision object; decided=false means no front matter restriction — fall through.
|
|
452
|
+
*
|
|
453
|
+
* An audience that leaves the subject out refuses every action, unless the
|
|
454
|
+
* subject holds `admin-system` (#1964, operator 2026-10-23): asked only
|
|
455
|
+
* then. `access[action]` has no such exception.
|
|
444
456
|
*/
|
|
445
457
|
private checkFrontmatterAccess;
|
|
458
|
+
/**
|
|
459
|
+
* Does the page's `audience` admit this subject (#1964)? True when the page
|
|
460
|
+
* names no audience, the subject matches it, or the subject holds
|
|
461
|
+
* `admin-system`. The same rule Tier 1 applies, asked by PageManager's
|
|
462
|
+
* write door, so a write cannot reach a page its writer is outside the
|
|
463
|
+
* audience of — whatever route it came by. Records nothing; the door does.
|
|
464
|
+
*
|
|
465
|
+
* @param userContext - the subject, as given
|
|
466
|
+
* @param metadata - the page's current frontmatter
|
|
467
|
+
*/
|
|
468
|
+
audienceAdmits(userContext: UserContext | null | undefined, metadata: PageFrontmatter | null | undefined): Promise<boolean>;
|
|
446
469
|
/**
|
|
447
470
|
* Record/audit an access decision.
|
|
448
471
|
* Accepts either a single object or positional args for backward compatibility.
|
|
@@ -121,6 +121,8 @@ export interface WikiConfig {
|
|
|
121
121
|
* counting every failure inside it (#1916). Default: 60.
|
|
122
122
|
*/
|
|
123
123
|
'ngdpbase.mail.failure-notice.window-minutes': number;
|
|
124
|
+
/** The most a message's attachments may total, in bytes (#1974). Default: 10485760. */
|
|
125
|
+
'ngdpbase.mail.attachments.max-bytes': number;
|
|
124
126
|
/**
|
|
125
127
|
* Anti-spam: max submissions per IP per `window-minutes` window before
|
|
126
128
|
* the 429 trips (#670 Phase E). Default: 5.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Consent to a site's documents (#1603): the shapes ConsentManager and its
|
|
3
|
+
* callers share, and what an add-on registers.
|
|
4
|
+
*/
|
|
5
|
+
/** Whose document it is: core's (`core`) or an add-on's slug. */
|
|
6
|
+
export type ConsentScope = string;
|
|
7
|
+
/** Where a person is asked to accept (#1603): `?scope=` (core when absent) and `?returnTo=`. */
|
|
8
|
+
export declare const CONSENT_PATH = "/consent";
|
|
9
|
+
/** The scope of core's own documents, `ngdpbase.consent.documents`. */
|
|
10
|
+
export declare const CORE_CONSENT_SCOPE = "core";
|
|
11
|
+
/**
|
|
12
|
+
* A document a person is asked to accept, as it stands now: a page whose
|
|
13
|
+
* front matter carries `version:` and `effective:`. Read from the page each
|
|
14
|
+
* time, never copied.
|
|
15
|
+
*/
|
|
16
|
+
export interface ConsentDocument {
|
|
17
|
+
/** The page's slug, which is also the document's id in a record (`terms-of-service`, `phr-privacy-policy`). */
|
|
18
|
+
id: string;
|
|
19
|
+
scope: ConsentScope;
|
|
20
|
+
/** The page's title, to show. */
|
|
21
|
+
title: string;
|
|
22
|
+
/** The page's `version:`, as a string. A change asks everyone again. */
|
|
23
|
+
version: string;
|
|
24
|
+
/** The page's `effective:` date (`YYYY-MM-DD`), or null when it names none. */
|
|
25
|
+
effective: string | null;
|
|
26
|
+
}
|
|
27
|
+
/** What a person saw when they accepted: each document and the version shown. */
|
|
28
|
+
export interface ConsentSeen {
|
|
29
|
+
id: string;
|
|
30
|
+
version: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* One acceptance, one line of the person's file. Append-only: a later
|
|
34
|
+
* version is a later line, nothing is rewritten.
|
|
35
|
+
*/
|
|
36
|
+
export interface ConsentRecord {
|
|
37
|
+
kind: 'accept';
|
|
38
|
+
document: string;
|
|
39
|
+
scope: ConsentScope;
|
|
40
|
+
version: string;
|
|
41
|
+
effective: string | null;
|
|
42
|
+
/** ISO 8601 time the person accepted. */
|
|
43
|
+
acceptedAt: string;
|
|
44
|
+
/**
|
|
45
|
+
* The account this was accepted on, by its creation time (#1937): a later
|
|
46
|
+
* account that takes the same username never inherits it.
|
|
47
|
+
*/
|
|
48
|
+
account?: string;
|
|
49
|
+
/** Who and how: the account, where from, and the way they were asked (`sign-up`, `sign-in`, `add-on`). */
|
|
50
|
+
context: {
|
|
51
|
+
by: string;
|
|
52
|
+
how: string;
|
|
53
|
+
origin: string;
|
|
54
|
+
ipAddress?: string;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/** One document's state for a person, for "Your consents" on /profile. */
|
|
58
|
+
export interface ConsentStatus {
|
|
59
|
+
document: ConsentDocument;
|
|
60
|
+
/** Their acceptance of the current version, or null: not accepted (never, or only an older version). */
|
|
61
|
+
current: ConsentRecord | null;
|
|
62
|
+
/** Their latest acceptance of any version, or null. */
|
|
63
|
+
latest: ConsentRecord | null;
|
|
64
|
+
}
|
|
65
|
+
/** What an add-on registers at load (#1603). */
|
|
66
|
+
export interface AddonConsentRegistration {
|
|
67
|
+
/**
|
|
68
|
+
* Page slugs of the add-on's documents, each starting `<slug>-`
|
|
69
|
+
* (`phr-terms-of-service`, `phr-privacy-policy`). Shipped as the add-on's
|
|
70
|
+
* own pages, with `version:` and `effective:` front matter.
|
|
71
|
+
*/
|
|
72
|
+
documents: string[];
|
|
73
|
+
/**
|
|
74
|
+
* URL path prefixes where a person uses the add-on in the browser
|
|
75
|
+
* (`/phr`). A person who has it on and has not accepted a changed version
|
|
76
|
+
* is asked there before going on. Optional: without it they are asked
|
|
77
|
+
* only when they next turn it on.
|
|
78
|
+
*/
|
|
79
|
+
paths?: string[];
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Thrown when a per-person add-on is turned on before its documents are
|
|
83
|
+
* accepted (#1603). `documents` names what is still to accept.
|
|
84
|
+
*/
|
|
85
|
+
export declare class ConsentRequiredError extends Error {
|
|
86
|
+
readonly scope: ConsentScope;
|
|
87
|
+
readonly documents: string[];
|
|
88
|
+
readonly name = "ConsentRequiredError";
|
|
89
|
+
constructor(scope: ConsentScope, documents: string[]);
|
|
90
|
+
}
|
|
@@ -45,6 +45,12 @@ declare module 'express-session' {
|
|
|
45
45
|
pendingRedirect?: string;
|
|
46
46
|
/** A verified outside identity no account has connected, waiting for the person to sign in another way (#1743). */
|
|
47
47
|
pendingConnection?: PendingConnection;
|
|
48
|
+
/**
|
|
49
|
+
* Set at sign-in when the person has core terms to accept at their current
|
|
50
|
+
* version (#1603); the consent gate holds the session to the prompt until
|
|
51
|
+
* they accept (or sign out). Cleared by the acceptance.
|
|
52
|
+
*/
|
|
53
|
+
consentPending?: boolean;
|
|
48
54
|
/** When this session last made a request, epoch ms — kept only while an idle timeout is set (#1546). */
|
|
49
55
|
lastActivity?: number;
|
|
50
56
|
/** When this session signed in, epoch ms: the start of `ngdpbase.session.max-age` (#1767). A re-authentication never moves it. */
|
|
@@ -15,6 +15,12 @@
|
|
|
15
15
|
* the add-on (#1973);
|
|
16
16
|
* - `setEnabledForPerson` sets this add-on's own per-person switch (#1963),
|
|
17
17
|
* bound to its slug, so it can never set another add-on's;
|
|
18
|
+
* - `registerShim` and `registerReceiver` (#1979, ICE) register into the import
|
|
19
|
+
* engine under this add-on's slug, and only the names its manifest declares;
|
|
20
|
+
* - the egress tools (`resolveEgressPolicy`, `validateUrl`,
|
|
21
|
+
* `createGuardedElasticsearchClient`, `refusedNodeMessage`) and `isAtLeast`
|
|
22
|
+
* are core's own pure functions, handed on unchanged, so an add-on never
|
|
23
|
+
* imports them from `dist/` (#1813);
|
|
18
24
|
* - the object is `Object.freeze`d, so no add-on can replace a value.
|
|
19
25
|
*
|
|
20
26
|
* Additive only: nothing here is removed or renamed without a major release.
|
|
@@ -26,8 +32,14 @@ import { actorOf, type ActorContext } from '../context/ActorContext.js';
|
|
|
26
32
|
import { jobContextFromRequest, jobContextFromSystem, type JobContext } from '../context/JobContext.js';
|
|
27
33
|
import { systemContext } from '../context/bootActions.js';
|
|
28
34
|
import { guardedFetch } from '../http/guardedFetch.js';
|
|
35
|
+
import { resolveEgressPolicy } from '../http/egressPolicy.js';
|
|
36
|
+
import { validateUrl } from '../http/ssrf.js';
|
|
37
|
+
import { createGuardedElasticsearchClient, refusedNodeMessage } from '../http/guardedElasticsearch.js';
|
|
38
|
+
import { isAtLeast } from './version.js';
|
|
29
39
|
import { recordAuditEvent } from './auditEvents.js';
|
|
30
40
|
import { addonAuditEventName } from './auditEventNames.js';
|
|
41
|
+
import type { IceReceiver, IceShim } from '../ice/types.js';
|
|
42
|
+
import type { AddonManifest } from '../managers/AddonsManager.js';
|
|
31
43
|
/** The values core offers an add-on, under the names they were imported as. */
|
|
32
44
|
export interface AddonHost {
|
|
33
45
|
readonly ApiContext: typeof ApiContext;
|
|
@@ -54,7 +66,35 @@ export interface AddonHost {
|
|
|
54
66
|
* does not say `perPerson: true`, or the person may not manage their profile.
|
|
55
67
|
*/
|
|
56
68
|
readonly setEnabledForPerson: PersonSwitchSetter;
|
|
69
|
+
/**
|
|
70
|
+
* The site's egress policy, read through `read` (a `ConfigurationManager.getProperty`
|
|
71
|
+
* reader) each time it is called, so an operator tightening it applies at once.
|
|
72
|
+
* Hand its `policy` to `guardedFetch` or `validateUrl`.
|
|
73
|
+
*/
|
|
74
|
+
readonly resolveEgressPolicy: typeof resolveEgressPolicy;
|
|
75
|
+
/** Whether a URL may be fetched under an egress policy, before any socket: http(s) and an allowed literal address. */
|
|
76
|
+
readonly validateUrl: typeof validateUrl;
|
|
77
|
+
/** An Elasticsearch client whose every connection is judged by the egress policy; the only way to build one. */
|
|
78
|
+
readonly createGuardedElasticsearchClient: typeof createGuardedElasticsearchClient;
|
|
79
|
+
/** The one boot-time line for an Elasticsearch node the egress policy refuses. */
|
|
80
|
+
readonly refusedNodeMessage: typeof refusedNodeMessage;
|
|
81
|
+
/** Whether an `x.y.z` version is at least another; throws on a version that is not `x.y.z`. */
|
|
82
|
+
readonly isAtLeast: typeof isAtLeast;
|
|
83
|
+
/**
|
|
84
|
+
* #1979 (ICE, D6): register an import shim, as `<slug>.<shim.name>`. Call it
|
|
85
|
+
* in `register`. Refused when the manifest's `shims` does not name it.
|
|
86
|
+
* Returns the shim's id; site configuration switches it on and off.
|
|
87
|
+
*/
|
|
88
|
+
readonly registerShim: (shim: IceShim) => string;
|
|
89
|
+
/**
|
|
90
|
+
* #1979 (ICE, D4): register a receiver — your manager's own door for what
|
|
91
|
+
* shims produce — as `<slug>.<receiver.name>`. Refused when the manifest's
|
|
92
|
+
* `receivers` does not name it. Returns the receiver's id.
|
|
93
|
+
*/
|
|
94
|
+
readonly registerReceiver: (receiver: IceReceiver) => string;
|
|
57
95
|
}
|
|
96
|
+
/** What the host may register for an add-on: the shim and receiver names its manifest declares (#1979). */
|
|
97
|
+
export type AddonIceDeclaration = Pick<AddonManifest, 'shims' | 'receivers'>;
|
|
58
98
|
/** Sets one add-on's per-person switch; core binds the slug when it builds the host (#1963). */
|
|
59
99
|
export type PersonSwitchSetter = (ctx: ActorContext, on: boolean) => Promise<void>;
|
|
60
100
|
/**
|
|
@@ -72,6 +112,9 @@ export declare function addonDeclaredAuditEvents(addonDefaults: Record<string, u
|
|
|
72
112
|
* @param setEnabledForPerson - This add-on's switch setter, slug already bound
|
|
73
113
|
* (`AddonsManager` supplies it); without one, the host's setter refuses
|
|
74
114
|
* @param engine - The running engine, which names the system principal for
|
|
75
|
-
* `systemContext
|
|
115
|
+
* `systemContext` and holds the import engine `registerShim` reaches;
|
|
116
|
+
* without one (a test) it is core's fallback principal, and no registry
|
|
117
|
+
* @param manifest - The add-on's manifest (`ngdpbase` in its package.json):
|
|
118
|
+
* the shims and receivers it may register (#1979)
|
|
76
119
|
*/
|
|
77
|
-
export declare function buildAddonHost(slug: string, addonDefaults: Record<string, unknown>, setEnabledForPerson?: PersonSwitchSetter, engine?: Parameters<typeof systemContext>[0]): AddonHost;
|
|
120
|
+
export declare function buildAddonHost(slug: string, addonDefaults: Record<string, unknown>, setEnabledForPerson?: PersonSwitchSetter, engine?: Parameters<typeof systemContext>[0], manifest?: AddonIceDeclaration | null): AddonHost;
|
|
@@ -15,6 +15,12 @@
|
|
|
15
15
|
* the add-on (#1973);
|
|
16
16
|
* - `setEnabledForPerson` sets this add-on's own per-person switch (#1963),
|
|
17
17
|
* bound to its slug, so it can never set another add-on's;
|
|
18
|
+
* - `registerShim` and `registerReceiver` (#1979, ICE) register into the import
|
|
19
|
+
* engine under this add-on's slug, and only the names its manifest declares;
|
|
20
|
+
* - the egress tools (`resolveEgressPolicy`, `validateUrl`,
|
|
21
|
+
* `createGuardedElasticsearchClient`, `refusedNodeMessage`) and `isAtLeast`
|
|
22
|
+
* are core's own pure functions, handed on unchanged, so an add-on never
|
|
23
|
+
* imports them from `dist/` (#1813);
|
|
18
24
|
* - the object is `Object.freeze`d, so no add-on can replace a value.
|
|
19
25
|
*
|
|
20
26
|
* Additive only: nothing here is removed or renamed without a major release.
|
|
@@ -25,6 +31,10 @@ import { actorOf } from '../context/ActorContext.js';
|
|
|
25
31
|
import { jobContextFromRequest, jobContextFromSystem } from '../context/JobContext.js';
|
|
26
32
|
import { systemContext } from '../context/bootActions.js';
|
|
27
33
|
import { guardedFetch } from '../http/guardedFetch.js';
|
|
34
|
+
import { resolveEgressPolicy } from '../http/egressPolicy.js';
|
|
35
|
+
import { validateUrl } from '../http/ssrf.js';
|
|
36
|
+
import { createGuardedElasticsearchClient, refusedNodeMessage } from '../http/guardedElasticsearch.js';
|
|
37
|
+
import { isAtLeast } from './version.js';
|
|
28
38
|
import { recordAuditEvent } from './auditEvents.js';
|
|
29
39
|
import { addonAuditEventName } from './auditEventNames.js';
|
|
30
40
|
import { auditDeclarationsFrom } from './auditRegistry.js';
|
|
@@ -56,10 +66,29 @@ function refuseUndeclared(slug, name, declared) {
|
|
|
56
66
|
* @param setEnabledForPerson - This add-on's switch setter, slug already bound
|
|
57
67
|
* (`AddonsManager` supplies it); without one, the host's setter refuses
|
|
58
68
|
* @param engine - The running engine, which names the system principal for
|
|
59
|
-
* `systemContext
|
|
69
|
+
* `systemContext` and holds the import engine `registerShim` reaches;
|
|
70
|
+
* without one (a test) it is core's fallback principal, and no registry
|
|
71
|
+
* @param manifest - The add-on's manifest (`ngdpbase` in its package.json):
|
|
72
|
+
* the shims and receivers it may register (#1979)
|
|
60
73
|
*/
|
|
61
|
-
export function buildAddonHost(slug, addonDefaults, setEnabledForPerson = noPersonSwitch, engine) {
|
|
74
|
+
export function buildAddonHost(slug, addonDefaults, setEnabledForPerson = noPersonSwitch, engine, manifest) {
|
|
62
75
|
const declared = addonDeclaredAuditEvents(addonDefaults);
|
|
76
|
+
const names = (list) => new Set(Array.isArray(list) ? list.filter((n) => typeof n === 'string') : []);
|
|
77
|
+
const iceDeclared = { shim: names(manifest?.shims), receiver: names(manifest?.receivers) };
|
|
78
|
+
const iceRegistry = (what, name) => {
|
|
79
|
+
const allowed = iceDeclared[what];
|
|
80
|
+
if (typeof name !== 'string' || !allowed.has(name)) {
|
|
81
|
+
const message = `Add-on '${slug}' may register only the ${what}s its manifest declares ("ngdpbase": { "${what}s": [...] } in package.json); ` +
|
|
82
|
+
`'${String(name)}' is not one of them (declared: ${allowed.size > 0 ? [...allowed].sort().join(', ') : 'none'}).`;
|
|
83
|
+
logger.error(`[addons] ${message}`);
|
|
84
|
+
throw new Error(message);
|
|
85
|
+
}
|
|
86
|
+
const registry = engine?.getManager('ImportManager');
|
|
87
|
+
if (typeof registry?.registerShim !== 'function' || typeof registry.registerReceiver !== 'function') {
|
|
88
|
+
throw new Error(`register${what === 'shim' ? 'Shim' : 'Receiver'} is not available: this host was built without a site's ImportManager`);
|
|
89
|
+
}
|
|
90
|
+
return registry;
|
|
91
|
+
};
|
|
63
92
|
const hostAddonAuditEventName = (name) => {
|
|
64
93
|
const checked = addonAuditEventName(name);
|
|
65
94
|
refuseUndeclared(slug, name, declared);
|
|
@@ -81,6 +110,13 @@ export function buildAddonHost(slug, addonDefaults, setEnabledForPerson = noPers
|
|
|
81
110
|
systemContext: (reason) => systemContext(engine, reason),
|
|
82
111
|
logger: logger.child({ addon: slug }),
|
|
83
112
|
guardedFetch,
|
|
84
|
-
setEnabledForPerson
|
|
113
|
+
setEnabledForPerson,
|
|
114
|
+
registerShim: (shim) => iceRegistry('shim', shim?.name).registerShim(slug, shim),
|
|
115
|
+
registerReceiver: (receiver) => iceRegistry('receiver', receiver?.name).registerReceiver(slug, receiver),
|
|
116
|
+
resolveEgressPolicy,
|
|
117
|
+
validateUrl,
|
|
118
|
+
createGuardedElasticsearchClient,
|
|
119
|
+
refusedNodeMessage,
|
|
120
|
+
isAtLeast
|
|
85
121
|
});
|
|
86
122
|
}
|
|
@@ -50,6 +50,10 @@ export declare const AUDIT_EVENT: {
|
|
|
50
50
|
readonly CONFIG_CHANGE: "config-change";
|
|
51
51
|
/** Every custom configuration value discarded; recorded before the reset, which cannot proceed without it */
|
|
52
52
|
readonly CONFIG_RESET: "config-reset";
|
|
53
|
+
/** A person accepted a document they are asked to accept (#1603): which document (core's terms-of-service or privacy-policy, or an add-on's), its version and effective date, and how (sign-up, sign-in, turning an add-on on). Recorded before the acceptance is stored, which cannot proceed without it: consent must be provable */
|
|
54
|
+
readonly CONSENT_GRANT: "consent-grant";
|
|
55
|
+
/** A person's acceptances ended (#1603): today only when their account is deleted, which removes their consent records; names the documents and versions they had accepted. Recorded before the records are removed */
|
|
56
|
+
readonly CONSENT_WITHDRAW: "consent-withdraw";
|
|
53
57
|
/** Contact created, changed, deleted or linked to an account; metadata names the kind (person or organization), the operation and the fields changed, never their values */
|
|
54
58
|
readonly CONTACT_MANAGE: "contact-manage";
|
|
55
59
|
/** A page's footnote list changed — action add, import, transfer, update or delete; page content written on someone's behalf (#1233) */
|
|
@@ -140,6 +144,22 @@ export declare const AUDIT_EVENT: {
|
|
|
140
144
|
readonly SHARE_EXTEND: "share-extend";
|
|
141
145
|
/** Share link revoked; pairs with token-revoke */
|
|
142
146
|
readonly SHARE_REVOKE: "share-revoke";
|
|
147
|
+
/** Sign-in approval: the browser that was waiting gave up the sign-in (#1532) */
|
|
148
|
+
readonly SIGNINAPPROVAL_ABANDON: "signinapproval-abandon";
|
|
149
|
+
/** Sign-in approval: the person pressed Approve (#1532) */
|
|
150
|
+
readonly SIGNINAPPROVAL_APPROVE: "signinapproval-approve";
|
|
151
|
+
/** Sign-in approval: the person pressed Deny; that sign-in will not complete (#1532) */
|
|
152
|
+
readonly SIGNINAPPROVAL_DENY: "signinapproval-deny";
|
|
153
|
+
/** Sign-in approval: a waiting sign-in expired before it was completed (#1532) */
|
|
154
|
+
readonly SIGNINAPPROVAL_EXPIRE: "signinapproval-expire";
|
|
155
|
+
/** Sign-in approval: the approval page was opened, with the opener's IP and browser; opening changes nothing (#1532) */
|
|
156
|
+
readonly SIGNINAPPROVAL_OPEN: "signinapproval-open";
|
|
157
|
+
/** Sign-in approval: an approval message was sent for a waiting sign-in, saying from where (IP, approximate place, browser) (#1532) */
|
|
158
|
+
readonly SIGNINAPPROVAL_REQUEST: "signinapproval-request";
|
|
159
|
+
/** Sign-in approval: the approval message could not be sent (transport error, or no channel configured) (#1532) */
|
|
160
|
+
readonly SIGNINAPPROVAL_SENDFAIL: "signinapproval-sendfail";
|
|
161
|
+
/** Sign-in approval: another approval message was asked for too soon and not sent (#1532) */
|
|
162
|
+
readonly SIGNINAPPROVAL_THROTTLE: "signinapproval-throttle";
|
|
143
163
|
/** A user's private store copy created at its door (#1414) — with key material when sealed; never the words or a key in the record */
|
|
144
164
|
readonly STORE_CREATE: "store-create";
|
|
145
165
|
/** A takeout imported into its owner's private store; a bulk write — who, which store, pages and files written or skipped */
|
|
@@ -57,6 +57,10 @@ export const AUDIT_EVENT = {
|
|
|
57
57
|
CONFIG_CHANGE: 'config-change',
|
|
58
58
|
/** Every custom configuration value discarded; recorded before the reset, which cannot proceed without it */
|
|
59
59
|
CONFIG_RESET: 'config-reset',
|
|
60
|
+
/** A person accepted a document they are asked to accept (#1603): which document (core's terms-of-service or privacy-policy, or an add-on's), its version and effective date, and how (sign-up, sign-in, turning an add-on on). Recorded before the acceptance is stored, which cannot proceed without it: consent must be provable */
|
|
61
|
+
CONSENT_GRANT: 'consent-grant',
|
|
62
|
+
/** A person's acceptances ended (#1603): today only when their account is deleted, which removes their consent records; names the documents and versions they had accepted. Recorded before the records are removed */
|
|
63
|
+
CONSENT_WITHDRAW: 'consent-withdraw',
|
|
60
64
|
/** Contact created, changed, deleted or linked to an account; metadata names the kind (person or organization), the operation and the fields changed, never their values */
|
|
61
65
|
CONTACT_MANAGE: 'contact-manage',
|
|
62
66
|
/** A page's footnote list changed — action add, import, transfer, update or delete; page content written on someone's behalf (#1233) */
|
|
@@ -147,6 +151,22 @@ export const AUDIT_EVENT = {
|
|
|
147
151
|
SHARE_EXTEND: 'share-extend',
|
|
148
152
|
/** Share link revoked; pairs with token-revoke */
|
|
149
153
|
SHARE_REVOKE: 'share-revoke',
|
|
154
|
+
/** Sign-in approval: the browser that was waiting gave up the sign-in (#1532) */
|
|
155
|
+
SIGNINAPPROVAL_ABANDON: 'signinapproval-abandon',
|
|
156
|
+
/** Sign-in approval: the person pressed Approve (#1532) */
|
|
157
|
+
SIGNINAPPROVAL_APPROVE: 'signinapproval-approve',
|
|
158
|
+
/** Sign-in approval: the person pressed Deny; that sign-in will not complete (#1532) */
|
|
159
|
+
SIGNINAPPROVAL_DENY: 'signinapproval-deny',
|
|
160
|
+
/** Sign-in approval: a waiting sign-in expired before it was completed (#1532) */
|
|
161
|
+
SIGNINAPPROVAL_EXPIRE: 'signinapproval-expire',
|
|
162
|
+
/** Sign-in approval: the approval page was opened, with the opener's IP and browser; opening changes nothing (#1532) */
|
|
163
|
+
SIGNINAPPROVAL_OPEN: 'signinapproval-open',
|
|
164
|
+
/** Sign-in approval: an approval message was sent for a waiting sign-in, saying from where (IP, approximate place, browser) (#1532) */
|
|
165
|
+
SIGNINAPPROVAL_REQUEST: 'signinapproval-request',
|
|
166
|
+
/** Sign-in approval: the approval message could not be sent (transport error, or no channel configured) (#1532) */
|
|
167
|
+
SIGNINAPPROVAL_SENDFAIL: 'signinapproval-sendfail',
|
|
168
|
+
/** Sign-in approval: another approval message was asked for too soon and not sent (#1532) */
|
|
169
|
+
SIGNINAPPROVAL_THROTTLE: 'signinapproval-throttle',
|
|
150
170
|
/** A user's private store copy created at its door (#1414) — with key material when sealed; never the words or a key in the record */
|
|
151
171
|
STORE_CREATE: 'store-create',
|
|
152
172
|
/** A takeout imported into its owner's private store; a bulk write — who, which store, pages and files written or skipped */
|