@jwilleke/ngdpbase-types 5.24.0 → 5.26.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 +2 -2
- package/lib/packages/ngdpbase-types/index.d.ts +6 -2
- package/lib/packages/ngdpbase-types/testing.d.ts +7 -2
- package/lib/packages/ngdpbase-types/testing.js +7 -2
- 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/managers/AddonsManager.d.ts +119 -2
- 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/FootnoteManager.d.ts +6 -6
- package/lib/src/managers/OrganizationManager.d.ts +8 -6
- package/lib/src/managers/PageManager.d.ts +16 -0
- 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 +8 -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 +51 -3
- package/lib/src/utils/addonHost.js +29 -2
- package/lib/src/utils/addonPageSync.d.ts +2 -2
- 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/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/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/package.json +6 -1
- package/lib/src/utils/administrators.d.ts +0 -21
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
* @see {@link https://github.com/jwilleke/ngdpbase/issues/396}
|
|
31
31
|
*/
|
|
32
32
|
import BaseManager from './BaseManager.js';
|
|
33
|
-
import type { BackupData,
|
|
33
|
+
import type { BackupData, BackupFolder } from './BaseManager.js';
|
|
34
34
|
import type { WikiEngine } from '../types/WikiEngine.js';
|
|
35
35
|
import type { PermissionSubject } from './UserManager.js';
|
|
36
36
|
import type { JobContext } from '../context/JobContext.js';
|
|
@@ -107,6 +107,12 @@ export declare function credentialLabel(label: unknown): string;
|
|
|
107
107
|
*/
|
|
108
108
|
export interface PendingSignIn {
|
|
109
109
|
handle: string;
|
|
110
|
+
/**
|
|
111
|
+
* The id every audit record of this sign-in's approval carries (#1532), so
|
|
112
|
+
* the steps read as one trail. Random and separate from the handle and the
|
|
113
|
+
* link token, which are secrets.
|
|
114
|
+
*/
|
|
115
|
+
id: string;
|
|
110
116
|
username: string;
|
|
111
117
|
first: AuthenticateResult;
|
|
112
118
|
/** The requesting browser's binding value (an HTTP-only cookie); every step must present it. */
|
|
@@ -115,15 +121,54 @@ export interface PendingSignIn {
|
|
|
115
121
|
expiresAt: number;
|
|
116
122
|
requestIp?: string;
|
|
117
123
|
requestUserAgent?: string;
|
|
124
|
+
/** Roughly where the request came from ("Columbus, United States"), or null when no source says (#1532). */
|
|
125
|
+
requestLocation?: string | null;
|
|
118
126
|
/** Private-store keys unlocked by the password, held until the sign-in completes or is dropped. */
|
|
119
127
|
privateStoreHandle?: string;
|
|
120
128
|
/** The second factor, once satisfied. */
|
|
121
129
|
approved?: SatisfiedFactor;
|
|
122
|
-
/** Refused by the person (
|
|
130
|
+
/** Refused by the person (Deny). */
|
|
123
131
|
denied?: boolean;
|
|
124
132
|
/** When the last approval message went out, for the resend limit. */
|
|
125
133
|
lastSentAt?: number;
|
|
126
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* What an approval request says about a waiting sign-in (#1532): one source
|
|
137
|
+
* for the message and the page.
|
|
138
|
+
*/
|
|
139
|
+
export interface ApprovalAbout {
|
|
140
|
+
/** The site's name (`ngdpbase.application-name`). */
|
|
141
|
+
site: string;
|
|
142
|
+
/** When it was asked, in the account holder's own time zone. */
|
|
143
|
+
when: string;
|
|
144
|
+
/** "Chrome on Windows", or null when the browser could not be named. */
|
|
145
|
+
device: string | null;
|
|
146
|
+
/** The IP address it came from (Express's `req.ip`, so `trust-proxy` applies). */
|
|
147
|
+
ip?: string;
|
|
148
|
+
/** Roughly where ("Columbus, United States"), or null when no source says. */
|
|
149
|
+
location: string | null;
|
|
150
|
+
}
|
|
151
|
+
/** What the approval page shows about a waiting sign-in (#1532). */
|
|
152
|
+
export interface ApprovalDetails extends ApprovalAbout {
|
|
153
|
+
username: string;
|
|
154
|
+
requestedAt: number;
|
|
155
|
+
userAgent?: string;
|
|
156
|
+
/** The sentence the message says, as HTML with every value escaped. */
|
|
157
|
+
sentenceHtml: string;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* The approval sentence (#1532, decided wording plus the IP and place):
|
|
161
|
+
* "A sign-in request for Willeke Family was sent at 2 Oct 2026, 14:02 EDT
|
|
162
|
+
* from Chrome on Windows at IP address 81.2.69.160, near Columbus, United
|
|
163
|
+
* States." `strong` marks the site and time, `text` every other value; for
|
|
164
|
+
* HTML both escape.
|
|
165
|
+
*/
|
|
166
|
+
export declare function approvalSentence(about: ApprovalAbout, strong?: (v: string) => string, text?: (v: string) => string): string;
|
|
167
|
+
/** Who opened the approval page or pressed its button (#1532): recorded, never trusted for anything. */
|
|
168
|
+
export interface ApprovalVisitor {
|
|
169
|
+
ip?: string;
|
|
170
|
+
userAgent?: string;
|
|
171
|
+
}
|
|
127
172
|
/** A second factor a person has enrolled and can use now. */
|
|
128
173
|
export interface AvailableSecondFactor {
|
|
129
174
|
id: 'email-link';
|
|
@@ -202,6 +247,18 @@ export type OutsideSignIn =
|
|
|
202
247
|
/** The state was already used: no second session. */
|
|
203
248
|
| {
|
|
204
249
|
outcome: 'replayed';
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* #1532: the provider's sign-in reached only AAL1 and the account has a
|
|
253
|
+
* second factor: held as a pending sign-in, as after a password. The route
|
|
254
|
+
* binds the browser (`binding`), keeps `handle` in the session and sends the
|
|
255
|
+
* person to the second-factor page; `next` is where they were going.
|
|
256
|
+
*/
|
|
257
|
+
| {
|
|
258
|
+
outcome: 'second-step';
|
|
259
|
+
handle: string;
|
|
260
|
+
binding: string;
|
|
261
|
+
next: string;
|
|
205
262
|
};
|
|
206
263
|
/** What the one completion step leaves for the route (#1743): where to go instead, when a pending identity waits to be confirmed. */
|
|
207
264
|
export interface SignInCompleted {
|
|
@@ -1030,6 +1087,12 @@ declare class AuthManager extends BaseManager {
|
|
|
1030
1087
|
privateStoreHandle?: string;
|
|
1031
1088
|
returnTo?: string;
|
|
1032
1089
|
}): Promise<SignInCompleted>;
|
|
1090
|
+
/**
|
|
1091
|
+
* Whether `username` has core documents to accept (#1603), asked of
|
|
1092
|
+
* ConsentManager. A store that cannot be read asks nothing, loudly: a
|
|
1093
|
+
* broken consent folder must not lock everyone out.
|
|
1094
|
+
*/
|
|
1095
|
+
private termsPendingFor;
|
|
1033
1096
|
/**
|
|
1034
1097
|
* Start the signed-in session's lifetime (#1767), on the new session after
|
|
1035
1098
|
* regeneration: `signedInAt`, from which `ngdpbase.session.max-age` caps it
|
|
@@ -1117,32 +1180,79 @@ declare class AuthManager extends BaseManager {
|
|
|
1117
1180
|
* is not offered. Empty means the sign-in is one step, as before.
|
|
1118
1181
|
*/
|
|
1119
1182
|
secondFactorsFor(username: string): AvailableSecondFactor[];
|
|
1183
|
+
/**
|
|
1184
|
+
* Whether a successful first step needs a second (#1532, operator
|
|
1185
|
+
* 2026-10-22): everyone who arrives with only a password behind them — a
|
|
1186
|
+
* password typed here, or an outside provider's sign-in at AAL1 — when the
|
|
1187
|
+
* account has a second factor it can use now. Never after an email link
|
|
1188
|
+
* (the second factor would go to the same inbox), and never after a sign-in
|
|
1189
|
+
* already at AAL2 or above (a passkey, a provider whose second factor the
|
|
1190
|
+
* admin trusts).
|
|
1191
|
+
*/
|
|
1192
|
+
needsSecondStep(result: AuthenticateResult): boolean;
|
|
1193
|
+
/**
|
|
1194
|
+
* Roughly where `req` came from, for the approval message (#1532):
|
|
1195
|
+
* Cloudflare's location headers when `ngdpbase.server.behind-cloudflare`,
|
|
1196
|
+
* else the GeoLite2 City file at `ngdpbase.server.geolite2-city-file`, else
|
|
1197
|
+
* null. Read when asked, so a change applies at the next sign-in. The IP is
|
|
1198
|
+
* Express's `req.ip`, which honours `ngdpbase.server.trust-proxy`.
|
|
1199
|
+
*/
|
|
1200
|
+
approximateLocation(req: Pick<Request, 'ip'> & {
|
|
1201
|
+
get?: (name: string) => string | undefined;
|
|
1202
|
+
}): Promise<string | null>;
|
|
1120
1203
|
/** Hold a sign-in whose first factor passed, until a second factor completes it. Returns its handle. */
|
|
1121
1204
|
beginTwoStep(first: AuthenticateResult, ctx: {
|
|
1122
1205
|
binding: string;
|
|
1123
1206
|
ip?: string;
|
|
1124
1207
|
userAgent?: string;
|
|
1208
|
+
location?: string | null;
|
|
1125
1209
|
privateStoreHandle?: string;
|
|
1126
1210
|
}): Promise<string>;
|
|
1127
1211
|
/** The pending sign-in, for the browser that started it; null when gone, expired, or another browser. */
|
|
1128
1212
|
pendingSignIn(handle: string | undefined, binding: string | undefined): PendingSignIn | null;
|
|
1213
|
+
/**
|
|
1214
|
+
* Record one step of a sign-in's approval (#1532): every record carries the
|
|
1215
|
+
* pending sign-in's `id` (never its handle or the link token), the account
|
|
1216
|
+
* it is for, and who took the step — the IP and browser of the request, the
|
|
1217
|
+
* opener or the presser. `user` is the account signing in: there is no
|
|
1218
|
+
* session yet, so no proxy (whom to act for is chosen after sign-in).
|
|
1219
|
+
*/
|
|
1220
|
+
private recordApproval;
|
|
1221
|
+
/**
|
|
1222
|
+
* HTML escaping for the approval message and page (#1532): PluginManager's
|
|
1223
|
+
* formatter (#1820), the one escapeHtml. Its absence is a wiring fault, not
|
|
1224
|
+
* a reason to send a value unescaped.
|
|
1225
|
+
*/
|
|
1226
|
+
private escapeHtml;
|
|
1227
|
+
/** Where the sign-in asked from, as the request, send and expiry records say it. */
|
|
1228
|
+
private static askedFrom;
|
|
1129
1229
|
/**
|
|
1130
1230
|
* Email a one-time approval link for the pending sign-in (#1523, #1532).
|
|
1131
1231
|
* The link only opens the approval page; the button there approves, so a
|
|
1132
1232
|
* mail scanner opening links approves nothing. Limited to one per minute.
|
|
1233
|
+
* Each outcome is recorded: sent (`signinapproval-request`), too soon
|
|
1234
|
+
* (`-throttle`), or not sent (`-sendfail`).
|
|
1133
1235
|
*/
|
|
1134
1236
|
sendEmailApproval(handle: string, binding: string): Promise<'sent' | 'too-soon' | 'unavailable' | 'failed'>;
|
|
1135
|
-
/**
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1237
|
+
/**
|
|
1238
|
+
* What the approval page shows about the waiting sign-in; null for an
|
|
1239
|
+
* unknown or used link. Opening the page changes nothing, but it is
|
|
1240
|
+
* recorded (`signinapproval-open`) with who opened it (#1532): a mail
|
|
1241
|
+
* scanner opens links too.
|
|
1242
|
+
*/
|
|
1243
|
+
approvalDetails(token: string, opener?: ApprovalVisitor): Promise<ApprovalDetails | null>;
|
|
1244
|
+
/**
|
|
1245
|
+
* What an approval request says about a waiting sign-in (#1532), one source
|
|
1246
|
+
* for the message and the page: the site's name, when it was asked in the
|
|
1247
|
+
* account holder's own time zone (their preference, else the site default),
|
|
1248
|
+
* the browser and device it came from (null when the header names
|
|
1249
|
+
* neither), its IP address, and roughly where (null when no source says).
|
|
1250
|
+
*/
|
|
1251
|
+
private approvalContext;
|
|
1142
1252
|
/** Approve the waiting sign-in from its link (the button press). Single use; returns the username, or null. */
|
|
1143
|
-
approveByToken(token: string): string | null
|
|
1144
|
-
/** Refuse the waiting sign-in from its link (
|
|
1145
|
-
denyByToken(token: string): string | null
|
|
1253
|
+
approveByToken(token: string, presser?: ApprovalVisitor): Promise<string | null>;
|
|
1254
|
+
/** Refuse the waiting sign-in from its link (Deny). Returns the username, or null. */
|
|
1255
|
+
denyByToken(token: string, presser?: ApprovalVisitor): Promise<string | null>;
|
|
1146
1256
|
/**
|
|
1147
1257
|
* The finished sign-in, once its second factor is satisfied — both factors,
|
|
1148
1258
|
* assessed together. Single use: the pending entry is gone after this.
|
|
@@ -1151,8 +1261,15 @@ declare class AuthManager extends BaseManager {
|
|
|
1151
1261
|
result: AuthenticateResult;
|
|
1152
1262
|
privateStoreHandle?: string;
|
|
1153
1263
|
} | null;
|
|
1264
|
+
/**
|
|
1265
|
+
* The waiting browser gave up the sign-in (Cancel, #1532): dropped, its
|
|
1266
|
+
* keys locked, and recorded as `signinapproval-abandon`.
|
|
1267
|
+
*/
|
|
1268
|
+
abandonTwoStep(handle: string, binding: string, from?: ApprovalVisitor): Promise<void>;
|
|
1154
1269
|
/** Drop a pending sign-in and lock any keys it held. */
|
|
1155
1270
|
dropPending(handle: string): void;
|
|
1271
|
+
/** A pending sign-in past its time: dropped, and recorded once as `signinapproval-expire` (#1532). */
|
|
1272
|
+
private expirePending;
|
|
1156
1273
|
private sweepPending;
|
|
1157
1274
|
/**
|
|
1158
1275
|
* Start enrolling the account's email address as a second factor (#1523).
|
|
@@ -1265,25 +1382,26 @@ declare class AuthManager extends BaseManager {
|
|
|
1265
1382
|
isEnabled(providerId: string): boolean;
|
|
1266
1383
|
/** Returns all registered providers (for admin UI). */
|
|
1267
1384
|
getProviders(): AuthProvider[];
|
|
1268
|
-
/**
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
*
|
|
1272
|
-
*
|
|
1273
|
-
*
|
|
1274
|
-
*
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
*
|
|
1281
|
-
*
|
|
1282
|
-
*
|
|
1283
|
-
* restore's notification names it as not
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1385
|
+
/** `ngdpbase.auth.credentials.file`, resolved: the credentials store. */
|
|
1386
|
+
private configuredCredentialsFile;
|
|
1387
|
+
/**
|
|
1388
|
+
* The provider list. The credentials store (#1876) — passkeys, TOTP,
|
|
1389
|
+
* verified email and phone, devices, linked identities and proxy grants —
|
|
1390
|
+
* is a declared file (`backupFolders`), which BackupManager writes beside
|
|
1391
|
+
* the document as `data.credentials`.
|
|
1392
|
+
*/
|
|
1393
|
+
backup(): Promise<BackupData>;
|
|
1394
|
+
/**
|
|
1395
|
+
* The credentials store, as its own file beside the backup document,
|
|
1396
|
+
* __always encrypted under `NGDPBASE_BACKUP_KEY`__
|
|
1397
|
+
* (`<stem>.credentials.tar.enc`; #1876, declared since #1985). Without the
|
|
1398
|
+
* key it is left out and the backup is reported incomplete; it is never in
|
|
1399
|
+
* a backup unencrypted. A backup that left it out stages nothing: the store
|
|
1400
|
+
* on the site stays, and the restore's notification names it as not
|
|
1401
|
+
* restored. The rows stay signed with `NGDPBASE_CREDENTIALS_KEY`, which is
|
|
1402
|
+
* not in the backup: a site restored under another credentials key holds
|
|
1403
|
+
* them in quarantine (#1633).
|
|
1404
|
+
*/
|
|
1405
|
+
backupFolders(): BackupFolder[];
|
|
1288
1406
|
}
|
|
1289
1407
|
export default AuthManager;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import BaseManager, { type BackupData, type
|
|
1
|
+
import BaseManager, { type BackupData, type BackupFolder } from './BaseManager.js';
|
|
2
2
|
import type { WikiEngine } from '../types/WikiEngine.js';
|
|
3
3
|
import { type JobContext } from '../context/JobContext.js';
|
|
4
4
|
import { type ScheduleInput } from '../utils/schedule.js';
|
|
@@ -432,21 +432,31 @@ declare class BackgroundJobManager extends BaseManager {
|
|
|
432
432
|
*/
|
|
433
433
|
private settleAfterRestore;
|
|
434
434
|
/**
|
|
435
|
-
*
|
|
436
|
-
*
|
|
437
|
-
*
|
|
438
|
-
* them. In the database: nothing here — it is in the database backup.
|
|
435
|
+
* Where job state is kept in files, before this manager has started
|
|
436
|
+
* (#1985): null when it is in the database, held for an unavailable
|
|
437
|
+
* database, or a test's own provider. After the start, `stateDir`.
|
|
439
438
|
*/
|
|
440
|
-
|
|
441
|
-
/**
|
|
442
|
-
|
|
439
|
+
private configuredFileStateDir;
|
|
440
|
+
/**
|
|
441
|
+
* File job state (`ngdpbase.jobs.state.dir`) is a declared folder (#1876,
|
|
442
|
+
* #1985): the state, run history and checkpoints as `<stem>.jobs.tar`
|
|
443
|
+
* beside the document, without the slot locks, which belong to the server
|
|
444
|
+
* that holds them. A backup without it (its job state was in the database)
|
|
445
|
+
* stages nothing for it. Job state in the database is in the database
|
|
446
|
+
* backup, and comes back with it.
|
|
447
|
+
*/
|
|
448
|
+
backupFolders(): BackupFolder[];
|
|
449
|
+
/** Where job state is when it is not a declared folder: the database, or nowhere while held (#1876). */
|
|
450
|
+
backup(): Promise<BackupData>;
|
|
451
|
+
/** Database job state comes back with the database; file job state is a declared folder BackupManager stages (#1876, #1985). */
|
|
443
452
|
restoreIsStaged(): boolean;
|
|
444
453
|
/**
|
|
445
|
-
*
|
|
446
|
-
*
|
|
447
|
-
*
|
|
454
|
+
* Whether file job state was put back from a backup at this start (#1876):
|
|
455
|
+
* BaseManager applied it before this manager opened it (#1985). The runs
|
|
456
|
+
* in the state it replaced, started since the backup, are read from where
|
|
457
|
+
* it was kept, for the restore's notification.
|
|
448
458
|
*/
|
|
449
|
-
private
|
|
459
|
+
private noteRestoreAtStart;
|
|
450
460
|
/**
|
|
451
461
|
* The job runs the live application database held, since the backup, when
|
|
452
462
|
* this start's restore replaced it (#1876): read from the copy DatabaseManager
|