@jwilleke/ngdpbase-types 5.41.1 → 5.43.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/lib/src/context/ApiContext.js +1 -1
- package/lib/src/context/BaseContext.d.ts +20 -20
- package/lib/src/context/BaseContext.js +41 -24
- package/lib/src/context/JobContext.d.ts +31 -0
- package/lib/src/context/JobContext.js +34 -1
- package/lib/src/managers/AddonsManager.d.ts +8 -28
- package/lib/src/managers/AgentTokenManager.d.ts +8 -0
- package/lib/src/managers/AttachmentManager.d.ts +59 -0
- package/lib/src/managers/AuditManager.d.ts +477 -0
- package/lib/src/managers/AuthManager.d.ts +247 -15
- package/lib/src/managers/AuthManager.js +588 -45
- package/lib/src/managers/ConfigurationManager.d.ts +15 -0
- package/lib/src/managers/ConsentManager.d.ts +15 -0
- package/lib/src/managers/ImportManager.d.ts +15 -0
- package/lib/src/managers/PageManager.d.ts +185 -23
- package/lib/src/managers/PolicyEvaluator.d.ts +99 -24
- package/lib/src/managers/ShareManager.d.ts +3 -2
- package/lib/src/managers/UserManager.d.ts +19 -5
- package/lib/src/managers/ValidationManager.d.ts +3 -1
- package/lib/src/providers/AgentTokenAuthProvider.js +1 -1
- package/lib/src/providers/BaseAttachmentProvider.d.ts +4 -0
- package/lib/src/providers/BaseAuditProvider.d.ts +492 -0
- package/lib/src/providers/BaseAuthProvider.d.ts +4 -0
- package/lib/src/providers/BaseCredentialsProvider.d.ts +2 -2
- package/lib/src/providers/BaseCredentialsProvider.js +1 -1
- package/lib/src/providers/BasicAttachmentProvider.d.ts +9 -0
- package/lib/src/providers/FileCredentialsProvider.js +1 -1
- package/lib/src/providers/NullAuditProvider.d.ts +82 -0
- package/lib/src/providers/TotpAuthProvider.d.ts +55 -0
- package/lib/src/providers/TotpAuthProvider.js +145 -0
- package/lib/src/security/PolicyDecisionPoint.d.ts +57 -1
- package/lib/src/security/PolicyInformationPoint.d.ts +86 -6
- package/lib/src/security/permissions.generated.d.ts +8 -0
- package/lib/src/types/Consent.d.ts +17 -4
- package/lib/src/types/Consent.js +11 -1
- package/lib/src/types/Page.d.ts +0 -7
- package/lib/src/types/Policy.d.ts +67 -6
- package/lib/src/types/Policy.js +6 -0
- package/lib/src/types/Provider.d.ts +18 -0
- package/lib/src/types/User.d.ts +6 -0
- package/lib/src/types/express.d.ts +22 -0
- package/lib/src/utils/addonPageSync.d.ts +0 -12
- package/lib/src/utils/auditChain.d.ts +83 -0
- package/lib/src/utils/auditEventNames.generated.d.ts +2 -0
- package/lib/src/utils/auditEventNames.generated.js +2 -0
- package/lib/src/utils/auditEvents.d.ts +2 -0
- package/lib/src/utils/auditEvents.js +3 -1
- package/lib/src/utils/auditHeadWitness.d.ts +97 -0
- package/lib/src/utils/auditLifecycle.d.ts +71 -0
- package/lib/src/utils/browserDescription.d.ts +18 -0
- package/lib/src/utils/browserDescription.js +12 -0
- package/lib/src/utils/frontmatterAccess.d.ts +15 -24
- package/lib/src/utils/legacyStoreKeys.d.ts +16 -0
- package/lib/src/utils/pageWritePolicies.d.ts +55 -0
- package/lib/src/utils/permissionCatalog.d.ts +2 -0
- package/lib/src/utils/permissionCatalog.js +19 -0
- package/lib/src/utils/policyAttributes.d.ts +141 -0
- package/lib/src/utils/policyAttributes.js +219 -0
- package/lib/src/utils/postureRecord.d.ts +73 -0
- package/lib/src/utils/privateStoreAccess.d.ts +24 -4
- package/lib/src/utils/privateStoreAccess.js +19 -4
- package/lib/src/utils/privateStorePath.d.ts +6 -0
- package/lib/src/utils/privateStorePath.js +8 -0
- package/lib/src/utils/securityPosture.d.ts +52 -0
- package/lib/src/utils/tlsConfig.d.ts +59 -0
- package/lib/src/utils/totpSeedCipher.d.ts +4 -0
- package/lib/src/utils/totpSeedCipher.js +45 -0
- package/lib/src/utils/username.d.ts +13 -2
- package/lib/src/utils/username.js +18 -3
- package/package.json +5 -2
|
@@ -253,7 +253,7 @@ export class ApiContext extends BaseContext {
|
|
|
253
253
|
// one is asked as #1745 always has: holds the permission, may not act now —
|
|
254
254
|
// exactly the condition under which the PDP writes STEP_UP_REASON.
|
|
255
255
|
const stepUp = Boolean(this.request) && await this.holdsPermission(permission);
|
|
256
|
-
await this.enforceDeny(permission, { permit: false, applicable: true, reason: stepUp ? STEP_UP_REASON : 'policy_deny' });
|
|
256
|
+
await this.enforceDeny(permission, { permit: false, applicable: true, reason: stepUp ? STEP_UP_REASON : 'policy_deny', policyIds: [] });
|
|
257
257
|
}
|
|
258
258
|
/**
|
|
259
259
|
* Turn a deny into the refusal (#1431) — the one implementation of refusal
|
|
@@ -67,19 +67,13 @@ export declare abstract class BaseContext {
|
|
|
67
67
|
* asked of the PDP (#1431 step 14): the token and share ceilings, then the
|
|
68
68
|
* policies, with deny policies and role expansion.
|
|
69
69
|
*
|
|
70
|
-
* For a check against a PAGE, ask
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* resource type can decide (e.g. `page-public`: may this person make pages
|
|
76
|
-
* of this system-category public?). It is the policy question alone: unlike
|
|
77
|
-
* `canAccess` it walks none of one page's own tiers (private owner,
|
|
78
|
-
* frontmatter), so a page's owner gets no pass on it.
|
|
70
|
+
* For a check against a PAGE, ask {@link hasPermissionOn}: the PDP then
|
|
71
|
+
* reads the page's own attributes (its system-category, its owner) from the
|
|
72
|
+
* PIP (#2255). Asked here, a page write (`page-edit`, `-delete`, `-rename`)
|
|
73
|
+
* means "on some category", which is right for an affordance or a token's
|
|
74
|
+
* scope and wrong for a door.
|
|
79
75
|
*/
|
|
80
|
-
hasPermission(action: string
|
|
81
|
-
'system-category'?: unknown;
|
|
82
|
-
}): Promise<boolean>;
|
|
76
|
+
hasPermission(action: string): Promise<boolean>;
|
|
83
77
|
/**
|
|
84
78
|
* Does this request's subject HOLD the permission (#1635)? Policy alone,
|
|
85
79
|
* without the step-up freshness rule — for affordances only (show this
|
|
@@ -90,16 +84,22 @@ export declare abstract class BaseContext {
|
|
|
90
84
|
/** One answer per question for this request — `hasPermission` and `hasPermissionOn` share it. */
|
|
91
85
|
private cached;
|
|
92
86
|
/**
|
|
93
|
-
* A capability asked about the page it is for (#1539). For a page in
|
|
94
|
-
* vault: the container rule first — the owner, never a role, the same
|
|
95
|
-
* as Tier 0 of the page tiers — then the capability with the page's
|
|
96
|
-
* `vault`,
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
* a named page asks this instead of
|
|
100
|
-
*
|
|
87
|
+
* A capability asked about the page it is for (#1539, #2255). For a page in
|
|
88
|
+
* a vault: the container rule first — the owner, never a role, the same
|
|
89
|
+
* rule as Tier 0 of the page tiers — then the capability with the page's
|
|
90
|
+
* `vault`, so a policy on the `vault` resource type (`vault-owner`)
|
|
91
|
+
* decides. For any other page, the PDP is asked about the page by name and
|
|
92
|
+
* reads its system-category and owner from the PIP, so the policies on its
|
|
93
|
+
* category decide. A route that acts on a named page asks this instead of
|
|
94
|
+
* the bare capability.
|
|
101
95
|
*/
|
|
102
96
|
hasPermissionOn(action: string, pageName: string): Promise<boolean>;
|
|
97
|
+
/**
|
|
98
|
+
* {@link hasPermissionOn} as an affordance (#2255): does the subject HOLD
|
|
99
|
+
* `action` on this page, policy alone, without the step-up rule? So the
|
|
100
|
+
* step-up fallback of a page-scoped check asks about the same page.
|
|
101
|
+
*/
|
|
102
|
+
holdsPermissionOn(action: string, pageName: string): Promise<boolean>;
|
|
103
103
|
/**
|
|
104
104
|
* The identifiers that match audience-style filters — every role, plus the
|
|
105
105
|
* username when there is one. Used where running the full evaluator per
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
import { parsePrivatePageName } from '../utils/privateStorePath.js';
|
|
3
|
-
import { permitsInVault } from '../utils/privateStoreAccess.js';
|
|
3
|
+
import { holdsInVault, permitsInVault } from '../utils/privateStoreAccess.js';
|
|
4
4
|
export class BaseContext {
|
|
5
5
|
/**
|
|
6
6
|
* The caller's subject as the middleware wrote it, or `null` for a context
|
|
@@ -57,25 +57,19 @@ export class BaseContext {
|
|
|
57
57
|
* asked of the PDP (#1431 step 14): the token and share ceilings, then the
|
|
58
58
|
* policies, with deny policies and role expansion.
|
|
59
59
|
*
|
|
60
|
-
* For a check against a PAGE, ask
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
* resource type can decide (e.g. `page-public`: may this person make pages
|
|
66
|
-
* of this system-category public?). It is the policy question alone: unlike
|
|
67
|
-
* `canAccess` it walks none of one page's own tiers (private owner,
|
|
68
|
-
* frontmatter), so a page's owner gets no pass on it.
|
|
60
|
+
* For a check against a PAGE, ask {@link hasPermissionOn}: the PDP then
|
|
61
|
+
* reads the page's own attributes (its system-category, its owner) from the
|
|
62
|
+
* PIP (#2255). Asked here, a page write (`page-edit`, `-delete`, `-rename`)
|
|
63
|
+
* means "on some category", which is right for an affordance or a token's
|
|
64
|
+
* scope and wrong for a door.
|
|
69
65
|
*/
|
|
70
|
-
async hasPermission(action
|
|
66
|
+
async hasPermission(action) {
|
|
71
67
|
const pdp = this.engineRef.getManager('PolicyDecisionPoint');
|
|
72
68
|
if (!pdp)
|
|
73
69
|
return false;
|
|
74
70
|
// #637: hand over the already-resolved subject so the PDP need not
|
|
75
71
|
// resolve the account and its roles again.
|
|
76
|
-
return this.cached(
|
|
77
|
-
? pdp.decide(this.getActor(), { action, resource: { type: 'page', id: '*' }, attributes }).then((d) => d.permit)
|
|
78
|
-
: pdp.permits(this.getActor(), action)));
|
|
72
|
+
return this.cached(action, () => pdp.permits(this.getActor(), action));
|
|
79
73
|
}
|
|
80
74
|
/**
|
|
81
75
|
* Does this request's subject HOLD the permission (#1635)? Policy alone,
|
|
@@ -101,24 +95,47 @@ export class BaseContext {
|
|
|
101
95
|
return promise;
|
|
102
96
|
}
|
|
103
97
|
/**
|
|
104
|
-
* A capability asked about the page it is for (#1539). For a page in
|
|
105
|
-
* vault: the container rule first — the owner, never a role, the same
|
|
106
|
-
* as Tier 0 of the page tiers — then the capability with the page's
|
|
107
|
-
* `vault`,
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
* a named page asks this instead of
|
|
111
|
-
*
|
|
98
|
+
* A capability asked about the page it is for (#1539, #2255). For a page in
|
|
99
|
+
* a vault: the container rule first — the owner, never a role, the same
|
|
100
|
+
* rule as Tier 0 of the page tiers — then the capability with the page's
|
|
101
|
+
* `vault`, so a policy on the `vault` resource type (`vault-owner`)
|
|
102
|
+
* decides. For any other page, the PDP is asked about the page by name and
|
|
103
|
+
* reads its system-category and owner from the PIP, so the policies on its
|
|
104
|
+
* category decide. A route that acts on a named page asks this instead of
|
|
105
|
+
* the bare capability.
|
|
112
106
|
*/
|
|
113
107
|
async hasPermissionOn(action, pageName) {
|
|
114
108
|
const name = parsePrivatePageName(pageName);
|
|
115
|
-
if (!name)
|
|
116
|
-
|
|
109
|
+
if (!name) {
|
|
110
|
+
const pdp = this.engineRef.getManager('PolicyDecisionPoint');
|
|
111
|
+
if (!pdp)
|
|
112
|
+
return false;
|
|
113
|
+
// A stand-in decider with no `decide` (test doubles) answers the capability, as `holdsPermission` does.
|
|
114
|
+
if (typeof pdp.decide !== 'function')
|
|
115
|
+
return this.hasPermission(action);
|
|
116
|
+
return this.cached(`${action}\u0000${pageName}`, () => pdp.decide(this.getActor(), { action, resource: { type: 'page', id: pageName } }).then((d) => d.permit));
|
|
117
|
+
}
|
|
117
118
|
if (!this.hasSubject())
|
|
118
119
|
return false;
|
|
119
120
|
const pdp = this.engineRef.getManager('PolicyDecisionPoint');
|
|
120
121
|
return this.cached(`${action}\u0000${pageName}`, () => permitsInVault(pdp, this.getActor(), action, { owner: name.owner, vault: name.store, resource: pageName }));
|
|
121
122
|
}
|
|
123
|
+
/**
|
|
124
|
+
* {@link hasPermissionOn} as an affordance (#2255): does the subject HOLD
|
|
125
|
+
* `action` on this page, policy alone, without the step-up rule? So the
|
|
126
|
+
* step-up fallback of a page-scoped check asks about the same page.
|
|
127
|
+
*/
|
|
128
|
+
async holdsPermissionOn(action, pageName) {
|
|
129
|
+
const pdp = this.engineRef.getManager('PolicyDecisionPoint');
|
|
130
|
+
if (!pdp || typeof pdp.holds !== 'function')
|
|
131
|
+
return this.hasPermissionOn(action, pageName);
|
|
132
|
+
const name = parsePrivatePageName(pageName);
|
|
133
|
+
if (!name)
|
|
134
|
+
return this.cached(`holds\u0000${action}\u0000${pageName}`, () => pdp.holds(this.getActor(), action, { resource: { type: 'page', id: pageName } }));
|
|
135
|
+
if (!this.hasSubject())
|
|
136
|
+
return false;
|
|
137
|
+
return this.cached(`holds\u0000${action}\u0000${pageName}`, () => holdsInVault(pdp, this.getActor(), action, { owner: name.owner, vault: name.store, resource: pageName }));
|
|
138
|
+
}
|
|
122
139
|
/**
|
|
123
140
|
* The identifiers that match audience-style filters — every role, plus the
|
|
124
141
|
* username when there is one. Used where running the full evaluator per
|
|
@@ -67,6 +67,15 @@ export interface JobContext {
|
|
|
67
67
|
viaProxy?: ProxyClaim;
|
|
68
68
|
/** The address the requesting call came from (#1179), when there was one. Provenance for the record. */
|
|
69
69
|
ipAddress?: string;
|
|
70
|
+
/**
|
|
71
|
+
* The assurance level (1 to 3) of the requester's session when the work was
|
|
72
|
+
* requested (#2261), which is the job's `subject:aal` whenever it runs. Taken
|
|
73
|
+
* from the requester's own session or token, never from an argument, so it
|
|
74
|
+
* cannot exceed it. Absent for work no session asked for (boot, a schedule,
|
|
75
|
+
* the command line) and for a job requested before it was stored: its
|
|
76
|
+
* `subject:aal` is missing and a condition on it fails closed.
|
|
77
|
+
*/
|
|
78
|
+
aal?: 1 | 2 | 3;
|
|
70
79
|
/** ISO 8601. When the work was requested, not when it ran. */
|
|
71
80
|
requestedAt: string;
|
|
72
81
|
/** Why, for origins with no person to ask. Free text, for the audit record. */
|
|
@@ -84,10 +93,23 @@ export interface JobContext {
|
|
|
84
93
|
/** The shape a request-bound context exposes; both WikiContext and ApiContext satisfy it. */
|
|
85
94
|
export interface RequestIdentity {
|
|
86
95
|
username?: string | null;
|
|
96
|
+
/** With `signIn`, `aalCap` and `viaToken`, what the level the job stores is read from (#2261). */
|
|
97
|
+
isAuthenticated?: boolean;
|
|
98
|
+
signIn?: {
|
|
99
|
+
aal?: unknown;
|
|
100
|
+
} | null;
|
|
101
|
+
aalCap?: number;
|
|
87
102
|
viaToken?: AgentTokenGrant;
|
|
88
103
|
viaShare?: ShareGrant;
|
|
89
104
|
viaProxy?: ProxyClaim;
|
|
90
105
|
ipAddress?: string;
|
|
106
|
+
/** An `ApiContext` holds the request's subject here, a `WikiContext` as `userContext`: the level is read from that, not from the wrapper (#2287). */
|
|
107
|
+
subject?: object | null;
|
|
108
|
+
userContext?: object | null;
|
|
109
|
+
/** A `JobContext` handed back in: it already holds the level its requester's session had. */
|
|
110
|
+
origin?: unknown;
|
|
111
|
+
requestedAt?: unknown;
|
|
112
|
+
aal?: unknown;
|
|
91
113
|
}
|
|
92
114
|
/**
|
|
93
115
|
* The username a request-origin job carries when the request had no identity.
|
|
@@ -150,6 +172,15 @@ export declare function jobContextFromOperator(username: string, reason: string,
|
|
|
150
172
|
* a context to pass, including code that runs with no request at all.
|
|
151
173
|
*/
|
|
152
174
|
export declare function jobContextFromSystem(systemPrincipal: string, reason: string, now?: Date): JobContext;
|
|
175
|
+
/**
|
|
176
|
+
* Boot work done in one user's own vaults, on that user's behalf (#1815) —
|
|
177
|
+
* the start-up vault upgrades. The job carries the owner's username, so the
|
|
178
|
+
* vault container rule admits it as it admits any job carrying who asked, and
|
|
179
|
+
* what it may do there is the owner's own rights, resolved by policy when
|
|
180
|
+
* each write is decided. Never the system principal, which no vault admits.
|
|
181
|
+
* Like every job it carries no key, so an encrypted vault stays closed to it.
|
|
182
|
+
*/
|
|
183
|
+
export declare function jobContextForOwner(owner: string, reason: string, now?: Date): JobContext;
|
|
153
184
|
/** As {@link jobContextFromSystem}, for work started by a timer rather than at boot. */
|
|
154
185
|
export declare function jobContextFromSchedule(systemPrincipal: string, reason: string, now?: Date): JobContext;
|
|
155
186
|
/**
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
* enqueued by a token-bearing request escapes the agent-token ceiling simply by
|
|
31
31
|
* becoming asynchronous.
|
|
32
32
|
*/
|
|
33
|
+
import { effectiveAal, toAal } from '../utils/policyAttributes.js';
|
|
33
34
|
/**
|
|
34
35
|
* The username a request-origin job carries when the request had no identity.
|
|
35
36
|
*
|
|
@@ -52,6 +53,7 @@ export function jobContextFromRequest(identity, now = new Date()) {
|
|
|
52
53
|
return {
|
|
53
54
|
username: identity?.username ?? ANONYMOUS_USERNAME,
|
|
54
55
|
origin: 'request',
|
|
56
|
+
...aalOf(identity),
|
|
55
57
|
...(identity?.viaToken ? { viaToken: identity.viaToken } : {}),
|
|
56
58
|
...(identity?.viaShare ? { viaShare: identity.viaShare } : {}),
|
|
57
59
|
...(identity?.viaProxy ? { viaProxy: proxyClaimForJob(identity.viaProxy) } : {}),
|
|
@@ -59,6 +61,24 @@ export function jobContextFromRequest(identity, now = new Date()) {
|
|
|
59
61
|
requestedAt: now.toISOString()
|
|
60
62
|
};
|
|
61
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* The level the requester's session or token has right now (#2261), as a field
|
|
66
|
+
* to store with the job; nothing when it has none. Read from the granting
|
|
67
|
+
* session's own subject whatever the caller passed (#2287): a request's subject
|
|
68
|
+
* as is, an `ApiContext`'s `subject`, a `WikiContext`'s `userContext`, or a
|
|
69
|
+
* `JobContext` handed back in, whose stored level is kept as it is.
|
|
70
|
+
*/
|
|
71
|
+
function aalOf(identity) {
|
|
72
|
+
if (!identity)
|
|
73
|
+
return {};
|
|
74
|
+
if (typeof identity.origin === 'string' && typeof identity.requestedAt === 'string') {
|
|
75
|
+
const kept = toAal(identity.aal);
|
|
76
|
+
return kept ? { aal: kept } : {};
|
|
77
|
+
}
|
|
78
|
+
const wrapped = identity.subject ?? identity.userContext;
|
|
79
|
+
const aal = effectiveAal(wrapped && typeof wrapped === 'object' ? (wrapped) : identity);
|
|
80
|
+
return aal ? { aal } : {};
|
|
81
|
+
}
|
|
62
82
|
/** The proxy claim without its key handle (#2047): the actor, grant and scopes stay for the ceiling and the record. */
|
|
63
83
|
function proxyClaimForJob(claim) {
|
|
64
84
|
const { keyHandle: _dropped, ...kept } = claim;
|
|
@@ -119,6 +139,17 @@ export function jobContextFromSystem(systemPrincipal, reason, now = new Date())
|
|
|
119
139
|
reason
|
|
120
140
|
};
|
|
121
141
|
}
|
|
142
|
+
/**
|
|
143
|
+
* Boot work done in one user's own vaults, on that user's behalf (#1815) —
|
|
144
|
+
* the start-up vault upgrades. The job carries the owner's username, so the
|
|
145
|
+
* vault container rule admits it as it admits any job carrying who asked, and
|
|
146
|
+
* what it may do there is the owner's own rights, resolved by policy when
|
|
147
|
+
* each write is decided. Never the system principal, which no vault admits.
|
|
148
|
+
* Like every job it carries no key, so an encrypted vault stays closed to it.
|
|
149
|
+
*/
|
|
150
|
+
export function jobContextForOwner(owner, reason, now = new Date()) {
|
|
151
|
+
return { username: owner, origin: 'boot', requestedAt: now.toISOString(), reason };
|
|
152
|
+
}
|
|
122
153
|
/** As {@link jobContextFromSystem}, for work started by a timer rather than at boot. */
|
|
123
154
|
export function jobContextFromSchedule(systemPrincipal, reason, now = new Date()) {
|
|
124
155
|
return { ...jobContextFromSystem(systemPrincipal, reason, now), origin: 'schedule' };
|
|
@@ -149,7 +180,9 @@ export function toPermissionSubject(ctx) {
|
|
|
149
180
|
...(ctx.viaShare ? { viaShare: ctx.viaShare } : {}),
|
|
150
181
|
...(ctx.viaProxy ? { viaProxy: ctx.viaProxy } : {}),
|
|
151
182
|
// #1999: the job a scheduled run acts as, for the PIP to resolve.
|
|
152
|
-
...(ctx.job ? { job: ctx.job } : {})
|
|
183
|
+
...(ctx.job ? { job: ctx.job } : {}),
|
|
184
|
+
// #2261: the level stored when it was requested, for `subject:aal`.
|
|
185
|
+
...(ctx.aal ? { grantedAal: ctx.aal } : {})
|
|
153
186
|
};
|
|
154
187
|
}
|
|
155
188
|
/** One line for a log or an audit record: who, from where, and how. */
|
|
@@ -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
|
|
@@ -596,34 +604,6 @@ declare class AddonsManager extends BaseManager {
|
|
|
596
604
|
* @param addonName Name of the add-on (for logging)
|
|
597
605
|
* @param addonPath Filesystem path to the add-on directory
|
|
598
606
|
*/
|
|
599
|
-
/**
|
|
600
|
-
* Default edit-protection for a seeded addon page (#971, addons.md §3).
|
|
601
|
-
*
|
|
602
|
-
* §3 settled the mechanism: the seeder stamps frontmatter `access`, which
|
|
603
|
-
* Tier 1 of the evaluator honours directly, needs no `PolicyEvaluator`
|
|
604
|
-
* change, and is hash-neutral (`pageSourceHash` covers the body only, so
|
|
605
|
-
* adding metadata cannot disturb the #920 reseed comparison).
|
|
606
|
-
*
|
|
607
|
-
* §9 settled *who owns what*, and the two must be read together. A blanket
|
|
608
|
-
* admin lock would contradict the operator's decision that domain content
|
|
609
|
-
* pages "should be normal pages, purely seeded by the addon" — the addon
|
|
610
|
-
* provides a starting corpus and then lets go. So the default follows §9's
|
|
611
|
-
* ownership column rather than locking everything:
|
|
612
|
-
*
|
|
613
|
-
* | Category | Purpose | Default |
|
|
614
|
-
* |---|---|---|
|
|
615
|
-
* | `system` | Feature UI, site chrome — infrastructure | admin-only edit |
|
|
616
|
-
* | `documentation` | Help / docs — addon-owned | admin-only edit |
|
|
617
|
-
* | `general` | Domain content, demo — **instance-owned** | no stamp |
|
|
618
|
-
* | anything else | unclassified, treated as addon-owned | admin-only edit |
|
|
619
|
-
*
|
|
620
|
-
* An addon can always override by declaring its own `access`, which is the
|
|
621
|
-
* documented escape hatch in §3 and the reason the default is a default.
|
|
622
|
-
*
|
|
623
|
-
* @param category - The page's resolved `system-category`
|
|
624
|
-
* @returns The access object to stamp, or undefined to leave the page unprotected
|
|
625
|
-
*/
|
|
626
|
-
private defaultAddonPageAccess;
|
|
627
607
|
/**
|
|
628
608
|
* Surface a page-level seed failure beyond the boot log (#951).
|
|
629
609
|
*
|
|
@@ -82,6 +82,14 @@ export interface AgentTokenRecord {
|
|
|
82
82
|
approvedBy?: string;
|
|
83
83
|
/** The proxy grant it was minted under. Ending that grant ends this token; it works only while the grant is in force. */
|
|
84
84
|
proxyGrantId?: string;
|
|
85
|
+
/**
|
|
86
|
+
* #2261: the assurance level (1 to 3) of the minting session when the token
|
|
87
|
+
* was made, which is `subject:aal` for every request the token makes. Taken
|
|
88
|
+
* from the session, never from the request, so it cannot exceed it. Absent
|
|
89
|
+
* on a token made before this was stored: its `subject:aal` is missing and a
|
|
90
|
+
* condition on it fails closed.
|
|
91
|
+
*/
|
|
92
|
+
aal?: 1 | 2 | 3;
|
|
85
93
|
}
|
|
86
94
|
/** A record safe to return over the API — no hash. */
|
|
87
95
|
export type AgentTokenPublic = Omit<AgentTokenRecord, 'hash'>;
|
|
@@ -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
|