@jwilleke/ngdpbase-types 5.41.0 → 5.42.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 (40) hide show
  1. package/lib/packages/media-date/index.d.ts +34 -0
  2. package/lib/src/managers/AddonsManager.d.ts +8 -0
  3. package/lib/src/managers/AttachmentManager.d.ts +59 -0
  4. package/lib/src/managers/AuditManager.d.ts +477 -0
  5. package/lib/src/managers/AuthManager.d.ts +247 -15
  6. package/lib/src/managers/AuthManager.js +588 -45
  7. package/lib/src/managers/ConfigurationManager.d.ts +6 -0
  8. package/lib/src/managers/ConsentManager.d.ts +15 -0
  9. package/lib/src/managers/ImportManager.d.ts +15 -0
  10. package/lib/src/managers/MediaManager.d.ts +9 -0
  11. package/lib/src/providers/BaseAttachmentProvider.d.ts +4 -0
  12. package/lib/src/providers/BaseAuditProvider.d.ts +492 -0
  13. package/lib/src/providers/BaseAuthProvider.d.ts +2 -0
  14. package/lib/src/providers/BaseCredentialsProvider.d.ts +2 -2
  15. package/lib/src/providers/BaseCredentialsProvider.js +1 -1
  16. package/lib/src/providers/BasicAttachmentProvider.d.ts +9 -0
  17. package/lib/src/providers/FileCredentialsProvider.js +1 -1
  18. package/lib/src/providers/FileSystemMediaProvider.d.ts +19 -0
  19. package/lib/src/providers/NullAuditProvider.d.ts +82 -0
  20. package/lib/src/providers/TotpAuthProvider.d.ts +55 -0
  21. package/lib/src/providers/TotpAuthProvider.js +145 -0
  22. package/lib/src/types/Consent.d.ts +17 -4
  23. package/lib/src/types/Consent.js +11 -1
  24. package/lib/src/types/Provider.d.ts +6 -0
  25. package/lib/src/types/express.d.ts +22 -0
  26. package/lib/src/utils/auditChain.d.ts +83 -0
  27. package/lib/src/utils/auditHeadWitness.d.ts +97 -0
  28. package/lib/src/utils/auditLifecycle.d.ts +71 -0
  29. package/lib/src/utils/browserDescription.d.ts +18 -0
  30. package/lib/src/utils/browserDescription.js +12 -0
  31. package/lib/src/utils/mediaFolderOverlap.d.ts +50 -0
  32. package/lib/src/utils/postureRecord.d.ts +73 -0
  33. package/lib/src/utils/privateStorePath.d.ts +6 -0
  34. package/lib/src/utils/privateStorePath.js +8 -0
  35. package/lib/src/utils/securityPosture.d.ts +52 -0
  36. package/lib/src/utils/tlsConfig.d.ts +59 -0
  37. package/lib/src/utils/totpSeedCipher.d.ts +4 -0
  38. package/lib/src/utils/totpSeedCipher.js +45 -0
  39. package/package.json +5 -2
  40. package/lib/src/providers/mediaFilenameDate.d.ts +0 -22
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Filename → capture-date parser, the one implementation of it.
3
+ *
4
+ * ngdpbase uses it when a media file carries no usable EXIF/QuickTime date, and
5
+ * mjs-media-handling uses a built copy of it to place and stamp files. Both must read a name the same way, which is why it is one package.
6
+ *
7
+ * It is a pure function of the name: no filesystem, no network, no server.
8
+ *
9
+ * Missing parts are filled with fixed, documented values — see FAKE_MONTH below
10
+ * and README.md. Callers that must know what was real read `parseFilenameDate`,
11
+ * whose `month`/`day`/`time` are null where the name did not carry them.
12
+ */
13
+ /** Written when the name gives a year but no month. */
14
+ export declare const FAKE_MONTH = 6;
15
+ /** Written when the name gives no day of the month. */
16
+ export declare const FAKE_DAY = 15;
17
+ /** Written when the name gives no time of day. */
18
+ export declare const FAKE_TIME = "11:59:00";
19
+ /** What a filename said, with null for each part it did not say. */
20
+ export interface FilenameDate {
21
+ year: number;
22
+ month: number | null;
23
+ day: number | null;
24
+ /** `HH:MM:SS`, only when the name carried a valid time. */
25
+ time: string | null;
26
+ /** Which rule matched, e.g. `filename:YYYY-MM-DD`. */
27
+ source: string;
28
+ }
29
+ /** What the name itself says, nothing filled in. Null when it says nothing. */
30
+ export declare function parseFilenameDate(filename: string): FilenameDate | null;
31
+ /** `YYYY-MM-DD HH:MM:SS`, with FAKE_MONTH, FAKE_DAY and FAKE_TIME where missing. */
32
+ export declare function fillFilenameDate(date: FilenameDate): string;
33
+ /** The capture date a filename gives, filled in; null when it gives none. */
34
+ export declare function parseDateFromFilename(filename: string): string | null;
@@ -175,6 +175,14 @@ export interface AddonProfileSection {
175
175
  title: string;
176
176
  /** Raw HTML rendered inside the card body; should contain form input fields. */
177
177
  html: string;
178
+ /**
179
+ * Font Awesome icon shown before the title in the card's header, such as
180
+ * `'fa-book'` (#1541). Optional; the host shows `fa-puzzle-piece` when it is
181
+ * missing or not a plain `fa-` name.
182
+ */
183
+ icon?: string;
184
+ /** Optional muted text shown after the title in the card's header (#1541). */
185
+ hint?: string;
178
186
  }
179
187
  /**
180
188
  * One entry per addon that contributed a profile section, returned by
@@ -56,6 +56,8 @@ interface BaseAttachmentProvider {
56
56
  filesInStoreForPage(location: StoreFileLocation, pageName: string): Promise<StoreFileEntry[]>;
57
57
  setFileMentionInStore(location: StoreFileLocation, id: string, pageName: string, mentioned: boolean): Promise<boolean>;
58
58
  deleteFileInStore(location: StoreFileLocation, id: string): Promise<StoreFileEntry | null>;
59
+ readStoreRenumbered(location: StoreFileLocation): Promise<Map<string, string>>;
60
+ noteStoreRenumbered(location: StoreFileLocation, pairs: ReadonlyMap<string, string>): Promise<void>;
59
61
  searchFilesInStore(location: StoreFileLocation, query: AssetQuery): Promise<AssetRecord[]>;
60
62
  privateRecordsInSharedIndex(): Array<{
61
63
  id: string;
@@ -117,6 +119,23 @@ export interface PutOptions {
117
119
  * record names the file by id only, so it is not recorded there.
118
120
  */
119
121
  importedFrom?: string;
122
+ /**
123
+ * A takeout import's file (#2188): the ids it had, the one to try first
124
+ * first, so a page still linking `/attachments/{id}` finds it again. The
125
+ * file is stored under the first that is free ({@link restoreIdIsFree});
126
+ * when none is, it gets a new id, as any upload does. Honoured only in a
127
+ * vault, never a shared folder or the shared pool; the import passes it
128
+ * only for one of its user's own stores. The stored id is in the returned
129
+ * metadata either way.
130
+ */
131
+ restoreIds?: readonly string[];
132
+ /**
133
+ * The ids files on the site held when the caller's run began, from
134
+ * {@link heldFileIds}, read once per run (#2188). With it, a restore checks
135
+ * the set instead of every store's folder on disk, and adds the id it
136
+ * stores to it.
137
+ */
138
+ heldIds?: Set<string>;
120
139
  }
121
140
  /**
122
141
  * The page-tied upload's options (#1396, #1398): today's callers name the page
@@ -216,6 +235,10 @@ declare class AttachmentManager extends BaseManager implements CatalogSource {
216
235
  /** What the last check for vault pages using shared-pool files found, and the degraded reason it set (#1842). */
217
236
  private poolFileFindings;
218
237
  private poolFileReason;
238
+ /** #2188: ids a restore has claimed and not yet written; one id is claimed by one put at a time. */
239
+ private readonly claimedRestoreIds;
240
+ /** #2188: ids a restore has stored since start, which a run's held ids read earlier may not list. */
241
+ private readonly restoredIds;
219
242
  private maxSize;
220
243
  private allowedTypes;
221
244
  /**
@@ -365,6 +388,42 @@ declare class AttachmentManager extends BaseManager implements CatalogSource {
365
388
  * store's file by id only, #2048), and FileManager, the owner, records it.
366
389
  */
367
390
  storeInPlace(ctx: ActorContext, place: FilePlace, file: PutFile, options?: PutOptions): Promise<StoredInPlace>;
391
+ /**
392
+ * Whether a file brought back by a takeout re-import may be stored under
393
+ * the id it had, `id` (#2188). Only an id of the shape a store gives
394
+ * (`isRestorableFileId`), and only when no file holds it: not the target
395
+ * store's index, not the shared pool, and no file on disk in any store of
396
+ * any owner or shared folder. A store names each file's bytes by its id
397
+ * (`{id}.ext`) in the clear, sealed or not, so another owner's sealed store
398
+ * is checked without its key. An id another file holds is never reused, so
399
+ * a takeout cannot claim another file's id, its own or anyone's.
400
+ *
401
+ * With `held` ({@link heldFileIds}, read once by the caller's run), the
402
+ * stores on disk are not read again: the id is free when the set lacks it,
403
+ * unless a restore stored that id since, which only the disk can settle.
404
+ */
405
+ private restoreIdIsFree;
406
+ /**
407
+ * The ids of every file on disk in any store of any owner or shared folder,
408
+ * by the names of their bytes (#2188). With `only`, stops at that id.
409
+ */
410
+ private storeIdsOnDisk;
411
+ /**
412
+ * The ids files in the site's stores hold now, read once, for a takeout
413
+ * import to pass as `PutOptions.heldIds` to each file it puts (#2188).
414
+ * Ids only, from the names of the bytes on disk; no store is opened.
415
+ */
416
+ heldFileIds(): Promise<Set<string>>;
417
+ /**
418
+ * The ids a takeout import gave the files of `ctx`'s own store `store` that
419
+ * could not keep their own, as takeout id → id here (#2188), or null when
420
+ * this context cannot open the store. Ids only.
421
+ */
422
+ ownStoreRenumbered(ctx: ActorContext, store: string): Promise<Map<string, string> | null>;
423
+ /** Adds `pairs` to {@link ownStoreRenumbered}'s, for a run that stored files under new ids (#2188). */
424
+ noteOwnStoreRenumbered(ctx: ActorContext, store: string, pairs: ReadonlyMap<string, string>): Promise<void>;
425
+ /** `ctx`'s own store `store`, as {@link ownStoreFingerprints} reaches it, or null. */
426
+ private ownStoreLocation;
368
427
  /**
369
428
  * The username an upload is recorded under (#1926): the person the context
370
429
  * names. Asked after policy has allowed the upload and before anything is
@@ -0,0 +1,477 @@
1
+ /**
2
+ * The caller may not query the audit log (#1116, #1165).
3
+ *
4
+ * A distinct type rather than a bare Error so a route can tell an
5
+ * authorization refusal from a genuine fault. #1165 was hard to diagnose
6
+ * precisely because it could not: the route's catch-all turned this refusal
7
+ * into `500 Error loading audit logs`, so "you may not read this" and
8
+ * "auditing is broken" were the same plain-text page.
9
+ *
10
+ * Matching on the message string would work today and break the first time
11
+ * somebody rewords it, which is why this is a type.
12
+ */
13
+ export declare class AuditQueryForbiddenError extends Error {
14
+ constructor(message: string);
15
+ }
16
+ /**
17
+ * AuditManager - Comprehensive audit trail system for access control and security monitoring
18
+ *
19
+ * Extends BaseManager following the JSPWiki modular manager pattern with provider architecture.
20
+ * Follows the provider pattern established in CacheManager, AttachmentManager, PageManager.
21
+ * Supports pluggable audit storage backends (file, database, cloud logging).
22
+ *
23
+ * @class AuditManager
24
+ * @extends BaseManager
25
+ *
26
+ * @property {BaseAuditProvider|null} provider - The active audit provider
27
+ * @property {string|null} providerClass - The class name of the loaded provider
28
+ *
29
+ * @see {@link BaseManager} for base functionality
30
+ * @see {@link FileAuditProvider} for default provider implementation
31
+ *
32
+ * @example
33
+ * const auditManager = engine.getManager('AuditManager');
34
+ * await auditManager.logAccess('admin', 'Main', 'view', 'granted');
35
+ */
36
+ import BaseManager, { type BackupData, type ManagerBackupOptions, type ManagerRestoreOptions } from './BaseManager.js';
37
+ import { type ActorContext } from '../context/ActorContext.js';
38
+ import type { PermissionSubject } from './UserManager.js';
39
+ import { type AuditEventName } from '../utils/auditEventNames.js';
40
+ import { WikiEngine } from '../types/WikiEngine.js';
41
+ import type { ReportedValues } from './AuthManager.js';
42
+ import type { AuditReport } from '../providers/BaseAuditProvider.js';
43
+ import { type AuditRecordOutcome } from '../utils/auditEvents.js';
44
+ /**
45
+ * Base audit event structure
46
+ */
47
+ interface AuditEvent {
48
+ eventType: AuditEventName;
49
+ user: string;
50
+ userId?: string;
51
+ sessionId?: string;
52
+ ipAddress?: string;
53
+ userAgent?: string;
54
+ resource?: string;
55
+ resourceType?: string;
56
+ action?: string;
57
+ result?: string;
58
+ reason?: string;
59
+ policyId?: string;
60
+ policyName?: string;
61
+ severity?: 'low' | 'medium' | 'high' | 'critical';
62
+ duration?: number;
63
+ context?: Record<string, unknown>;
64
+ metadata?: Record<string, unknown>;
65
+ timestamp?: string;
66
+ }
67
+ /**
68
+ * User information for audit events
69
+ */
70
+ interface AuditUser {
71
+ username?: string;
72
+ id?: string;
73
+ roles?: string[];
74
+ attributes?: Record<string, unknown>;
75
+ }
76
+ /**
77
+ * Context for access control decisions
78
+ */
79
+ interface AccessContext {
80
+ user?: AuditUser;
81
+ sessionId?: string;
82
+ ipAddress?: string;
83
+ userAgent?: string;
84
+ resource?: string;
85
+ resourceType?: string;
86
+ action?: string;
87
+ requestMethod?: string;
88
+ requestPath?: string;
89
+ timestamp?: string;
90
+ }
91
+ /**
92
+ * Policy information
93
+ */
94
+ interface PolicyInfo {
95
+ id: string;
96
+ name: string;
97
+ priority?: number;
98
+ }
99
+ /**
100
+ * Context for authentication events
101
+ */
102
+ interface AuthenticationContext {
103
+ username?: string;
104
+ ipAddress?: string;
105
+ userAgent?: string;
106
+ loginMethod?: string;
107
+ attemptedUsername?: string;
108
+ /** #1633: an outside provider's reported `amr` / `acr`, and whether it is trusted; never a token. */
109
+ reported?: ReportedValues;
110
+ /** #1523 slice 1c: the second-factor prompt was skipped on a remembered browser. Not a factor. */
111
+ knownDevice?: boolean;
112
+ /** #1523: the RFC 8176 methods of a completed sign-in, so a remembered browser's row can be checked against it. */
113
+ amr?: string[];
114
+ }
115
+ /**
116
+ * What a remembered browser's row is checked against (#1523): the fields of
117
+ * one `authentication-success` record, never the rest of it.
118
+ */
119
+ export interface AuthenticationRecordSummary {
120
+ id: string;
121
+ user: string;
122
+ loginMethod?: string;
123
+ amr?: string[];
124
+ }
125
+ /**
126
+ * Context for security events
127
+ */
128
+ interface SecurityContext {
129
+ /**
130
+ * The context the event happened in (#1762 slice 4): the row's "who" is
131
+ * built from it (`actorOf`), so under a proxy grant it names both people.
132
+ * Mandatory: null only for a caller with no context (a sign-in throttle,
133
+ * before anyone is signed in). Never stored on the row itself.
134
+ */
135
+ subject: ActorContext | null;
136
+ /** The named user, for a caller with no context (a sign-in throttle, before anyone is signed in). */
137
+ user?: AuditUser;
138
+ ipAddress?: string;
139
+ userAgent?: string;
140
+ [key: string]: unknown;
141
+ }
142
+ /**
143
+ * Filters for searching audit logs
144
+ */
145
+ interface AuditFilters {
146
+ user?: string;
147
+ /** The person who actually acted, under a proxy grant (#1762). */
148
+ actor?: string;
149
+ eventType?: string;
150
+ result?: string;
151
+ severity?: 'low' | 'medium' | 'high' | 'critical';
152
+ startDate?: string | Date;
153
+ endDate?: string | Date;
154
+ resource?: string;
155
+ action?: string;
156
+ }
157
+ /**
158
+ * Options for searching audit logs
159
+ */
160
+ interface AuditSearchOptions {
161
+ limit?: number;
162
+ offset?: number;
163
+ sortBy?: string;
164
+ sortOrder?: 'asc' | 'desc';
165
+ }
166
+ /**
167
+ * Search results structure
168
+ */
169
+ interface AuditSearchResults {
170
+ results: AuditEvent[];
171
+ total: number;
172
+ limit: number;
173
+ offset: number;
174
+ hasMore: boolean;
175
+ }
176
+ /**
177
+ * Audit statistics structure
178
+ */
179
+ interface AuditStats {
180
+ totalEvents: number;
181
+ eventsByType: Record<string, number>;
182
+ eventsByResult: Record<string, number>;
183
+ eventsBySeverity: Record<string, number>;
184
+ eventsByUser: Record<string, number>;
185
+ recentActivity: AuditEvent[];
186
+ securityIncidents: number;
187
+ }
188
+ /**
189
+ * Who is asking for audit records (#1116): the request's subject (#1762
190
+ * slice 4), so the decision is the PDP's for the subject as it is — under a
191
+ * proxy grant, bounded by the grant and the actor — and the export row names
192
+ * both people. The permission decision is derived inside AuditManager, never
193
+ * passed in.
194
+ */
195
+ export type AuditQueryCaller = PermissionSubject;
196
+ declare class AuditManager extends BaseManager {
197
+ private provider;
198
+ private providerClass;
199
+ /**
200
+ * Creates a new AuditManager instance
201
+ *
202
+ * @constructor
203
+ * @param {WikiEngine} engine - The wiki engine instance
204
+ */
205
+ constructor(engine: WikiEngine);
206
+ /**
207
+ * Initialize the AuditManager and load the configured provider
208
+ *
209
+ * @async
210
+ * @param {Record<string, unknown>} [config={}] - Configuration object (unused, reads from ConfigurationManager)
211
+ * @returns {Promise<void>}
212
+ * @throws {Error} If ConfigurationManager is not available or provider fails to load
213
+ */
214
+ initialize(config?: Record<string, unknown>): Promise<void>;
215
+ /**
216
+ * Load the audit provider dynamically
217
+ *
218
+ * @private
219
+ * @returns {Promise<void>}
220
+ */
221
+ /**
222
+ * Whether auditing is running degraded — configured but not actually working
223
+ * (#1118).
224
+ *
225
+ * Distinct from auditing being OFF. An operator who selects
226
+ * `nullauditprovider`, or sets `ngdpbase.audit.enabled: false`, has made a
227
+ * decision that is on the record. An instance whose configured provider
228
+ * failed has not, and until this existed it had no way to say so: the server
229
+ * booted healthy, the audit page rendered empty, and the only thing that
230
+ * would have recorded the problem was the thing that failed.
231
+ */
232
+ private degraded;
233
+ /** True when audit is configured but not working. */
234
+ isDegraded(): boolean;
235
+ /** Why audit is degraded, or null. */
236
+ degradedReason(): string | null;
237
+ /**
238
+ * What this instance's auditing actually does right now (#1118).
239
+ *
240
+ * Reportable rather than inferable: a security property that has to be
241
+ * deduced from configuration is not one an instance can be assessed on.
242
+ */
243
+ getAuditPosture(): {
244
+ provider: string;
245
+ configured: string;
246
+ degraded: boolean;
247
+ reason: string | null;
248
+ guarantees: AuditReport | null;
249
+ };
250
+ /**
251
+ * Begin a new audit chain, recording that the old one was abandoned (#1124).
252
+ *
253
+ * Exposed so the operator command can reach it. Never called automatically —
254
+ * a system that silently repairs its own audit chain is worse than one that
255
+ * stays visibly broken.
256
+ */
257
+ restartAuditChain(reason: string, ctx: ActorContext): Promise<string>;
258
+ /**
259
+ * Fall back to the inert provider, recording that we did (#1118).
260
+ *
261
+ * @private
262
+ */
263
+ private degradeToNull;
264
+ private loadProvider;
265
+ /**
266
+ * Normalize provider name to PascalCase class name
267
+ *
268
+ * @param {string} providerName - Lowercase provider name (e.g., 'fileauditprovider')
269
+ * @returns {string} PascalCase class name (e.g., 'FileAuditProvider')
270
+ * @private
271
+ */
272
+ private normalizeProviderName;
273
+ /**
274
+ * Log an audit event
275
+ *
276
+ * @param {AuditEvent} auditEvent - Audit event data
277
+ * @returns {Promise<string>} Event ID
278
+ */
279
+ logAuditEvent(auditEvent: AuditEvent): Promise<string>;
280
+ /**
281
+ * The one door for the helpers below (#1205): through `recordAuditEvent`,
282
+ * so the `enabled` switch, the on-failure rule, and the outcome are the same as for
283
+ * every other emitter. Calling `logAuditEvent` directly skipped all three.
284
+ */
285
+ private record;
286
+ /** {@link record}, and the stored record's id when it was recorded (#1523). */
287
+ private recordWithId;
288
+ /**
289
+ * Log access control decision
290
+ *
291
+ * @param {AccessContext} context - Access context
292
+ * @param {string} result - 'allow', 'deny', 'error'
293
+ * @param {string} reason - Reason for the decision
294
+ * @param {PolicyInfo | null} policy - Policy that made the decision
295
+ * @returns What became of the record (#1205)
296
+ */
297
+ logAccessDecision(context: AccessContext, result: string, reason: string, policy?: PolicyInfo | null): Promise<AuditRecordOutcome>;
298
+ /**
299
+ * Log policy evaluation
300
+ *
301
+ * @param {AccessContext} context - Evaluation context
302
+ * @param {PolicyInfo[]} policies - Policies evaluated
303
+ * @param {string} finalResult - Final result
304
+ * @param {number} duration - Evaluation duration in ms
305
+ * @returns What became of the record (#1205)
306
+ */
307
+ logPolicyEvaluation(context: AccessContext, policies: PolicyInfo[], finalResult: string, duration: number): Promise<AuditRecordOutcome>;
308
+ /**
309
+ * Log authentication event
310
+ *
311
+ * @param {AuthenticationContext} context - Authentication context
312
+ * @param {string} result - 'success', 'failure', 'logout'
313
+ * @param {string} reason - Reason for result
314
+ * @returns What became of the record (#1205), and its id when it was
315
+ * recorded: a remembered browser's row names its sign-in's record (#1523)
316
+ */
317
+ logAuthentication(context: AuthenticationContext, result: string, reason: string): Promise<{
318
+ outcome: AuditRecordOutcome;
319
+ id?: string;
320
+ }>;
321
+ /**
322
+ * The `authentication-success` record with this id, or null (#1523): a
323
+ * remembered browser counts only while the record of the sign-in that
324
+ * remembered it says the same as its row. Only the fields that check needs
325
+ * leave this method, so it is not an audit query and asks no caller; a
326
+ * record of any other kind is null. Null too when the provider cannot look
327
+ * a record up by id.
328
+ */
329
+ authenticationRecord(id: string): Promise<AuthenticationRecordSummary | null>;
330
+ /**
331
+ * Log security event
332
+ *
333
+ * @param {SecurityContext} context - Security context
334
+ * @param {string} eventType - Type of security event
335
+ * @param {string} severity - 'low', 'medium', 'high', 'critical'
336
+ * @param {string} description - Event description
337
+ * @returns What became of the record (#1205)
338
+ */
339
+ logSecurityEvent(context: SecurityContext, eventType: string, severity: 'low' | 'medium' | 'high' | 'critical', description: string): Promise<AuditRecordOutcome>;
340
+ /**
341
+ * #1116: the gate on every audit query, inside the door.
342
+ *
343
+ * The audit list was the counterexample to the list-gate rule: this manager
344
+ * applied no authorization of its own, safe only because one admin-system
345
+ * check gated the whole page at the route — exactly the reasoning that
346
+ * stops working the moment a second caller appears. The caller supplies its
347
+ * subject; the decision is derived here, by the PDP (#1762 slice 4: the
348
+ * subject as it is, so a person acting for someone is bounded by the grant
349
+ * and by what they hold themselves, never by the represented person's roles
350
+ * alone). Fail closed: no caller, no username, no PDP, or no permission all
351
+ * refuse. The audit log has no per-record narrowing, and a list that
352
+ * cannot be narrowed refuses rather than returning everything.
353
+ */
354
+ private assertQueryCallerAllowed;
355
+ /**
356
+ * Search audit logs
357
+ *
358
+ * @param {AuditFilters} filters - Search filters
359
+ * @param {AuditSearchOptions} options - Search options
360
+ * @param {AuditQueryCaller} caller - Who is asking: the request's subject (the decision is made here)
361
+ * @returns {Promise<AuditSearchResults>} Search results
362
+ */
363
+ searchAuditLogs(filters?: AuditFilters, options?: AuditSearchOptions, caller?: AuditQueryCaller): Promise<AuditSearchResults>;
364
+ /**
365
+ * The audit entries about one person, newest first, a page at a time
366
+ * (#1537): the one audit log, filtered to the rows where the person is the
367
+ * `user` (the account acted on, or the person represented under a proxy
368
+ * grant), the `actor` (who actually did it under a proxy grant), or the
369
+ * owner of the resource the row names (`metadata.owner`: the user's
370
+ * identity `@id` on rows from #2041 on, their username on older rows).
371
+ * Nothing is removed from the log by reading it.
372
+ *
373
+ * Decided by the PDP with the caller's subject: a person asking about
374
+ * themselves needs `profile-manage` (granted to every account); asking about
375
+ * anyone else is reading the audit log, `admin-system`. Under a proxy grant
376
+ * the subject's username is the person represented, and the PDP bounds the
377
+ * answer by the grant. Refused with {@link AuditQueryForbiddenError}.
378
+ *
379
+ * @param person - The username whose entries are listed
380
+ * @param options - `limit` (capped by the provider) and `offset`
381
+ * @param caller - Who is asking: the request's subject
382
+ */
383
+ searchEntriesAbout(person: string, options?: {
384
+ limit?: number;
385
+ offset?: number;
386
+ }, caller?: AuditQueryCaller): Promise<AuditSearchResults>;
387
+ /**
388
+ * Get audit statistics
389
+ *
390
+ * @param {AuditFilters} filters - Optional filters
391
+ * @returns {Promise<AuditStats>} Audit statistics
392
+ */
393
+ getAuditStats(filters?: AuditFilters, caller?: AuditQueryCaller): Promise<AuditStats>;
394
+ /**
395
+ * Export audit logs
396
+ *
397
+ * @param {AuditFilters} filters - Export filters
398
+ * @param {string} format - Export format ('json', 'csv')
399
+ * @param {AuditQueryCaller} caller - Who is asking (#1116)
400
+ * @returns {Promise<string>} Exported data
401
+ */
402
+ exportAuditLogs(filters?: AuditFilters, format?: string, caller?: AuditQueryCaller): Promise<string>;
403
+ /**
404
+ * Flush audit queue to disk
405
+ *
406
+ * @returns {Promise<void>}
407
+ */
408
+ flushAuditQueue(): Promise<void>;
409
+ /**
410
+ * Clean up old audit logs based on retention policy
411
+ *
412
+ * @returns {Promise<void>}
413
+ */
414
+ cleanupOldLogs(): Promise<void>;
415
+ /**
416
+ * The most recent record of a lifecycle phase, or null (#1149).
417
+ *
418
+ * A provider that cannot be searched returns null rather than throwing: an
419
+ * instance must still boot when its audit history is unreadable, and
420
+ * `assessPreviousRun` already treats missing evidence as `none` or `unknown`
421
+ * rather than inventing a verdict.
422
+ */
423
+ private lastLifecycleRecord;
424
+ /**
425
+ * Record that this process started, and what became of the previous one
426
+ * (#1149).
427
+ *
428
+ * The assessment is read BEFORE the new start is written, or the record
429
+ * being written would be the one it found.
430
+ */
431
+ private recordStart;
432
+ /**
433
+ * Record the security posture, and compare it against the previous start
434
+ * (#1156, D19).
435
+ *
436
+ * `config-change` audits what goes through `setProperty()`. This closes the
437
+ * two holes that leaves: an `app-custom-config.json` edited directly on disk
438
+ * emits nothing, and the state an instance STARTED in was never stated.
439
+ * Comparing consecutive boots surfaces an edit nothing observed.
440
+ *
441
+ * Emitted after `system-start`, deliberately: the start record establishes
442
+ * that this run began, and the posture record says what it began with.
443
+ */
444
+ private recordPosture;
445
+ /** Record that this process is stopping (#1149). */
446
+ private recordShutdown;
447
+ private emitLifecycle;
448
+ /** The active provider's file backup and staged restore, when it keeps files (FileAuditProvider, #1876). */
449
+ private fileProvider;
450
+ /**
451
+ * The audit log in a backup (#1876): the provider's own files beside the
452
+ * document, with the hash chain they carry (`FileAuditProvider.backupFiles`).
453
+ * A provider that keeps no files is not in the file backup: the database
454
+ * provider's records are in the application database, which DatabaseManager
455
+ * backs up after writing the audit queue (#1537).
456
+ */
457
+ backup(options?: ManagerBackupOptions): Promise<BackupData>;
458
+ /**
459
+ * Staged for the next start with the rest of a site restore (#1876); the
460
+ * live log keeps recording until then.
461
+ *
462
+ * A site whose audit provider keeps no files (the database provider, #1537)
463
+ * stages nothing from a backup's audit files and does not fail the restore:
464
+ * its audit records are restored with the databases, and the file part is
465
+ * left out, so BackupManager reports "the audit log" as not restored. The
466
+ * files stay in the backup.
467
+ */
468
+ restore(backupData: BackupData, options?: ManagerRestoreOptions): Promise<void>;
469
+ restoreIsStaged(): boolean;
470
+ /**
471
+ * Shutdown the audit manager
472
+ *
473
+ * @returns {Promise<void>}
474
+ */
475
+ shutdown(): Promise<void>;
476
+ }
477
+ export default AuditManager;