@memberjunction/storage 6.2.0-edge.4 → 6.2.0-edge.5
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/dist/FileStorageEngine.d.ts +45 -1
- package/dist/FileStorageEngine.d.ts.map +1 -1
- package/dist/FileStorageEngine.js +72 -4
- package/dist/FileStorageEngine.js.map +1 -1
- package/dist/StorageAccessEvaluator.d.ts +196 -0
- package/dist/StorageAccessEvaluator.d.ts.map +1 -0
- package/dist/StorageAccessEvaluator.js +435 -0
- package/dist/StorageAccessEvaluator.js.map +1 -0
- package/dist/config.d.ts +22 -10
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -0
- package/dist/config.js.map +1 -1
- package/dist/drivers/AWSFileStorage.d.ts +8 -0
- package/dist/drivers/AWSFileStorage.d.ts.map +1 -1
- package/dist/drivers/AWSFileStorage.js +11 -0
- package/dist/drivers/AWSFileStorage.js.map +1 -1
- package/dist/drivers/AzureFileStorage.d.ts +9 -0
- package/dist/drivers/AzureFileStorage.d.ts.map +1 -1
- package/dist/drivers/AzureFileStorage.js +22 -24
- package/dist/drivers/AzureFileStorage.js.map +1 -1
- package/dist/drivers/GoogleDriveFileStorage.d.ts +49 -2
- package/dist/drivers/GoogleDriveFileStorage.d.ts.map +1 -1
- package/dist/drivers/GoogleDriveFileStorage.js +80 -16
- package/dist/drivers/GoogleDriveFileStorage.js.map +1 -1
- package/dist/generic/FileStorageBase.d.ts +17 -1
- package/dist/generic/FileStorageBase.d.ts.map +1 -1
- package/dist/generic/FileStorageBase.js +18 -0
- package/dist/generic/FileStorageBase.js.map +1 -1
- package/dist/generic/ObjectKeys.d.ts +61 -0
- package/dist/generic/ObjectKeys.d.ts.map +1 -0
- package/dist/generic/ObjectKeys.js +82 -0
- package/dist/generic/ObjectKeys.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
- package/readme.md +74 -0
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { IMetadataProvider, UserInfo } from '@memberjunction/core';
|
|
2
2
|
import { BaseSingleton } from '@memberjunction/global';
|
|
3
|
-
import { FileStorageEngineBase, StorageAccountWithProvider, MJFileStorageAccountEntity, MJFileStorageProviderEntity } from '@memberjunction/core-entities';
|
|
3
|
+
import { FileStorageEngineBase, StorageAccountWithProvider, MJFileEntity, MJFileStorageAccountEntity, MJFileStorageProviderEntity } from '@memberjunction/core-entities';
|
|
4
4
|
import { FileStorageBase } from './generic/FileStorageBase.js';
|
|
5
|
+
import { StorageAccountAccess } from './StorageAccessEvaluator.js';
|
|
5
6
|
/**
|
|
6
7
|
* Options for uploading a file to MJ Storage.
|
|
7
8
|
*/
|
|
@@ -51,6 +52,17 @@ export interface UploadFileResult {
|
|
|
51
52
|
/** The storage provider that was used */
|
|
52
53
|
Provider: MJFileStorageProviderEntity;
|
|
53
54
|
}
|
|
55
|
+
/** Where an `MJ: Files` row says its bytes live — what {@link FileStorageEngine.ResolveFileObject} reads. */
|
|
56
|
+
export type StorageFileLocation = Pick<MJFileEntity, 'ProviderID' | 'ProviderKey' | 'Name'>;
|
|
57
|
+
/** The account, driver and object key that serve an `MJ: Files` row, as {@link FileStorageEngine.ResolveFileObject} returns them. */
|
|
58
|
+
export interface ResolvedFileObject {
|
|
59
|
+
/** The storage account the row's provider resolves to (the provider's first account). */
|
|
60
|
+
Account: MJFileStorageAccountEntity;
|
|
61
|
+
/** An initialized driver for that account. */
|
|
62
|
+
Driver: FileStorageBase;
|
|
63
|
+
/** The object key the row resolves to ({@link StorageObjectKeyOf}): `ProviderKey`, else `Name`. */
|
|
64
|
+
ObjectKey: string;
|
|
65
|
+
}
|
|
54
66
|
/**
|
|
55
67
|
* Server-side file storage engine providing high-level operations for uploading,
|
|
56
68
|
* downloading, and managing files in MJ Storage.
|
|
@@ -166,12 +178,44 @@ export declare class FileStorageEngine extends BaseSingleton<FileStorageEngine>
|
|
|
166
178
|
* - Decrypting credentials via the Credential Engine
|
|
167
179
|
* - Setting up OAuth token refresh callbacks for providers like Box
|
|
168
180
|
*
|
|
181
|
+
* **No permission check.** The driver cache is process-wide and this method does not ask whether `contextUser` may
|
|
182
|
+
* use the account. Code acting for a user must call {@link StorageAccessEvaluator.AssertAccountAccess} (or
|
|
183
|
+
* `AccessibleAccountIDs`) first — the MJServer storage routes, the File Storage core actions and the search lane do —
|
|
184
|
+
* and, going from an `MJ: Files` row to its object, should use {@link ResolveFileObject} instead.
|
|
185
|
+
*
|
|
169
186
|
* @param accountId - The FileStorageAccount ID to get a driver for
|
|
170
187
|
* @param contextUser - User context for credential decryption (used for on-demand init)
|
|
171
188
|
* @returns An initialized, ready-to-use FileStorageBase driver
|
|
172
189
|
* @throws Error if the account is not found or driver initialization fails
|
|
173
190
|
*/
|
|
174
191
|
GetDriver(accountId: string, contextUser: UserInfo): Promise<FileStorageBase>;
|
|
192
|
+
/**
|
|
193
|
+
* Resolves an `MJ: Files` row to the account, driver and object key that serve its bytes — and refuses unless
|
|
194
|
+
* `contextUser` may use that account for `access` and may read every `MJ: Files` row that tracks the object.
|
|
195
|
+
*
|
|
196
|
+
* Every route that goes from a file ID to bytes or a URL uses this: the row names only a PROVIDER (and `ProviderKey`
|
|
197
|
+
* / `Name` are written by whoever saved the row), so without the account gate a row could reach an account the caller
|
|
198
|
+
* is refused, and without the tracked-file rule a second row could alias an object whose own row the caller cannot
|
|
199
|
+
* read. Load the row as the caller first — this decides storage access, not whether the caller may see the row.
|
|
200
|
+
*
|
|
201
|
+
* @param file - The row's storage location (`ProviderID`, `ProviderKey`, `Name`)
|
|
202
|
+
* @param contextUser - The caller the decision is for
|
|
203
|
+
* @param access - `Read` to download or sign a URL; `Write` to delete, rename or overwrite the object
|
|
204
|
+
* @param provider - The metadata provider to read permissions through; defaults to `Metadata.Provider`
|
|
205
|
+
* @returns The resolved object, or `null` when the row's provider has no storage account
|
|
206
|
+
* @throws StorageAccountAccessDeniedError when the account refuses `access`;
|
|
207
|
+
* TrackedFileAccessDeniedError when a row tracking the object is not readable by the caller
|
|
208
|
+
*/
|
|
209
|
+
ResolveFileObject(file: StorageFileLocation, contextUser: UserInfo, access?: StorageAccountAccess, provider?: IMetadataProvider): Promise<ResolvedFileObject | null>;
|
|
210
|
+
/** The first storage account on `providerID`, refreshing the cache once when none is found; `null` when there is none. */
|
|
211
|
+
private firstAccountForProvider;
|
|
212
|
+
/**
|
|
213
|
+
* Reloads the shared account cache after a lookup missed, as the MJ system user. A forced `Config` loads as the user
|
|
214
|
+
* it is given, and `BaseEngine` empties a config that user cannot read (`MJ: File Storage Accounts`), so a restricted
|
|
215
|
+
* caller forcing the reload would blank the cache, and re-initialize every driver, for everyone. With no system user
|
|
216
|
+
* nothing is reloaded and the miss stands.
|
|
217
|
+
*/
|
|
218
|
+
RefreshAfterMiss(provider?: IMetadataProvider): Promise<void>;
|
|
175
219
|
/**
|
|
176
220
|
* Uploads a file to MJ Storage and creates an `MJ: Files` entity record.
|
|
177
221
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileStorageEngine.d.ts","sourceRoot":"","sources":["../src/FileStorageEngine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAsB,QAAQ,
|
|
1
|
+
{"version":3,"file":"FileStorageEngine.d.ts","sourceRoot":"","sources":["../src/FileStorageEngine.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { LogError, Metadata } from '@memberjunction/core';
|
|
1
|
+
import { LogError, Metadata, WellKnownUserSource } from '@memberjunction/core';
|
|
2
2
|
import { BaseSingleton } from '@memberjunction/global';
|
|
3
3
|
import { FileStorageEngineBase } from '@memberjunction/core-entities';
|
|
4
|
+
import { StorageObjectKeyOf } from './generic/ObjectKeys.js';
|
|
4
5
|
import { InitializeDriverWithAccountCredentials } from './util.js';
|
|
6
|
+
import { StorageAccessEvaluator } from './StorageAccessEvaluator.js';
|
|
5
7
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
6
8
|
// Engine
|
|
7
9
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -230,6 +232,11 @@ export class FileStorageEngine extends BaseSingleton {
|
|
|
230
232
|
* - Decrypting credentials via the Credential Engine
|
|
231
233
|
* - Setting up OAuth token refresh callbacks for providers like Box
|
|
232
234
|
*
|
|
235
|
+
* **No permission check.** The driver cache is process-wide and this method does not ask whether `contextUser` may
|
|
236
|
+
* use the account. Code acting for a user must call {@link StorageAccessEvaluator.AssertAccountAccess} (or
|
|
237
|
+
* `AccessibleAccountIDs`) first — the MJServer storage routes, the File Storage core actions and the search lane do —
|
|
238
|
+
* and, going from an `MJ: Files` row to its object, should use {@link ResolveFileObject} instead.
|
|
239
|
+
*
|
|
233
240
|
* @param accountId - The FileStorageAccount ID to get a driver for
|
|
234
241
|
* @param contextUser - User context for credential decryption (used for on-demand init)
|
|
235
242
|
* @returns An initialized, ready-to-use FileStorageBase driver
|
|
@@ -244,7 +251,7 @@ export class FileStorageEngine extends BaseSingleton {
|
|
|
244
251
|
// On-demand fallback: account wasn't in cache (failed init, added late, etc.)
|
|
245
252
|
let resolved = this.Base.GetAccountWithProvider(accountId);
|
|
246
253
|
if (!resolved) {
|
|
247
|
-
await this.
|
|
254
|
+
await this.RefreshAfterMiss();
|
|
248
255
|
resolved = this.Base.GetAccountWithProvider(accountId);
|
|
249
256
|
}
|
|
250
257
|
if (!resolved) {
|
|
@@ -259,6 +266,64 @@ export class FileStorageEngine extends BaseSingleton {
|
|
|
259
266
|
this._driverCache.set(accountId, driver);
|
|
260
267
|
return driver;
|
|
261
268
|
}
|
|
269
|
+
/**
|
|
270
|
+
* Resolves an `MJ: Files` row to the account, driver and object key that serve its bytes — and refuses unless
|
|
271
|
+
* `contextUser` may use that account for `access` and may read every `MJ: Files` row that tracks the object.
|
|
272
|
+
*
|
|
273
|
+
* Every route that goes from a file ID to bytes or a URL uses this: the row names only a PROVIDER (and `ProviderKey`
|
|
274
|
+
* / `Name` are written by whoever saved the row), so without the account gate a row could reach an account the caller
|
|
275
|
+
* is refused, and without the tracked-file rule a second row could alias an object whose own row the caller cannot
|
|
276
|
+
* read. Load the row as the caller first — this decides storage access, not whether the caller may see the row.
|
|
277
|
+
*
|
|
278
|
+
* @param file - The row's storage location (`ProviderID`, `ProviderKey`, `Name`)
|
|
279
|
+
* @param contextUser - The caller the decision is for
|
|
280
|
+
* @param access - `Read` to download or sign a URL; `Write` to delete, rename or overwrite the object
|
|
281
|
+
* @param provider - The metadata provider to read permissions through; defaults to `Metadata.Provider`
|
|
282
|
+
* @returns The resolved object, or `null` when the row's provider has no storage account
|
|
283
|
+
* @throws StorageAccountAccessDeniedError when the account refuses `access`;
|
|
284
|
+
* TrackedFileAccessDeniedError when a row tracking the object is not readable by the caller
|
|
285
|
+
*/
|
|
286
|
+
async ResolveFileObject(file, contextUser, access = 'Read', provider) {
|
|
287
|
+
const account = await this.firstAccountForProvider(file.ProviderID, contextUser, provider);
|
|
288
|
+
if (!account) {
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
const evaluator = StorageAccessEvaluator.Instance;
|
|
292
|
+
await evaluator.AssertAccountAccess(account.ID, contextUser, access, provider);
|
|
293
|
+
const driver = await this.GetDriver(account.ID, contextUser);
|
|
294
|
+
const objectKey = StorageObjectKeyOf(file);
|
|
295
|
+
await evaluator.AssertTrackedObjectsReadable(file.ProviderID, [objectKey], contextUser, provider, driver);
|
|
296
|
+
return { Account: account, Driver: driver, ObjectKey: objectKey };
|
|
297
|
+
}
|
|
298
|
+
/** The first storage account on `providerID`, refreshing the cache once when none is found; `null` when there is none. */
|
|
299
|
+
async firstAccountForProvider(providerID, contextUser, provider) {
|
|
300
|
+
await this.Config(false, contextUser, provider);
|
|
301
|
+
let accounts = this.Base.GetAccountsByProviderID(providerID);
|
|
302
|
+
if (accounts.length === 0) {
|
|
303
|
+
await this.RefreshAfterMiss(provider);
|
|
304
|
+
accounts = this.Base.GetAccountsByProviderID(providerID);
|
|
305
|
+
}
|
|
306
|
+
return accounts[0] ?? null;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Reloads the shared account cache after a lookup missed, as the MJ system user. A forced `Config` loads as the user
|
|
310
|
+
* it is given, and `BaseEngine` empties a config that user cannot read (`MJ: File Storage Accounts`), so a restricted
|
|
311
|
+
* caller forcing the reload would blank the cache, and re-initialize every driver, for everyone. With no system user
|
|
312
|
+
* nothing is reloaded and the miss stands.
|
|
313
|
+
*/
|
|
314
|
+
async RefreshAfterMiss(provider) {
|
|
315
|
+
const md = provider ?? Metadata.Provider;
|
|
316
|
+
let systemUser = null;
|
|
317
|
+
try {
|
|
318
|
+
systemUser = await WellKnownUserSource.Instance.GetSystemUser(md);
|
|
319
|
+
}
|
|
320
|
+
catch (error) {
|
|
321
|
+
LogError(`FileStorageEngine: resolving the system user for a cache refresh failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
322
|
+
}
|
|
323
|
+
if (systemUser) {
|
|
324
|
+
await this.Config(true, systemUser, md);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
262
327
|
/**
|
|
263
328
|
* Uploads a file to MJ Storage and creates an `MJ: Files` entity record.
|
|
264
329
|
*
|
|
@@ -292,8 +357,8 @@ export class FileStorageEngine extends BaseSingleton {
|
|
|
292
357
|
// 1. Resolve storage account
|
|
293
358
|
let resolved = this.ResolveStorageAccount(storageAccountId);
|
|
294
359
|
if (!resolved) {
|
|
295
|
-
// Attempt a
|
|
296
|
-
await this.
|
|
360
|
+
// Attempt a refresh from the database in case the account was recently added or the cache was stale
|
|
361
|
+
await this.RefreshAfterMiss(md);
|
|
297
362
|
resolved = this.ResolveStorageAccount(storageAccountId);
|
|
298
363
|
}
|
|
299
364
|
if (!resolved) {
|
|
@@ -306,6 +371,9 @@ export class FileStorageEngine extends BaseSingleton {
|
|
|
306
371
|
const rawPrefix = options.pathPrefix ?? `artifacts/${new Date().toISOString().slice(0, 10)}/${crypto.randomUUID()}`;
|
|
307
372
|
const cleanPrefix = rawPrefix.replace(/\.\./g, '').replace(/^[/\\]+/, '').replace(/[\x00-\x1f\x7f]/g, '').replace(/[/\\]+/g, '/').replace(/\/+$/, '') || 'artifacts';
|
|
308
373
|
const storagePath = `${cleanPrefix}/${cleanFileName}`;
|
|
374
|
+
// Never overwrite an object that backs an `MJ: Files` row the caller cannot read (a caller-chosen pathPrefix can
|
|
375
|
+
// name one). The account gate is the caller's job — this method uploads to the account it is given.
|
|
376
|
+
await StorageAccessEvaluator.Instance.AssertTrackedObjectsReadable(resolved.provider.ID, [storagePath], contextUser, md, driver);
|
|
309
377
|
const uploaded = await driver.PutObject(storagePath, content, mimeType);
|
|
310
378
|
if (!uploaded) {
|
|
311
379
|
throw new Error(`FileStorageEngine.UploadFile: PutObject returned false for path '${storagePath}'`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileStorageEngine.js","sourceRoot":"","sources":["../src/FileStorageEngine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,QAAQ,EAAE,QAAQ,EAAY,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"FileStorageEngine.js","sourceRoot":"","sources":["../src/FileStorageEngine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The one place MJ decides who may use a file storage account, and which tracked files in it a user may
|
|
3
|
+
* read.
|
|
4
|
+
*
|
|
5
|
+
* `MJ: File Storage Account Permissions` rows grant `CanRead` and/or `CanWrite` on a storage account to Everyone, to a
|
|
6
|
+
* Role, or to a single User. Every consumer of that model asks {@link StorageAccessEvaluator}: the storage GraphQL routes
|
|
7
|
+
* (MJServer `FileResolver`, `ArtifactFileResolver`), the File Storage core actions and the agent file handler, the
|
|
8
|
+
* search lane (`StorageSearchProvider`), the search engine's late permission filter, and every route that goes from an
|
|
9
|
+
* `MJ: Files` row to its object (through `FileStorageEngine.ResolveFileObject`).
|
|
10
|
+
*
|
|
11
|
+
* **Evaluated per call.** Nothing is snapshotted: each question reads the permission rows for exactly the accounts it
|
|
12
|
+
* names, so a grant or a revocation is seen by the next call, and the answer for one user never depends on which user
|
|
13
|
+
* happened to ask first. Reads set `IgnoreMaxRows`, so the entity's row cap can never drop an account's rows (which the
|
|
14
|
+
* zero-rows rule would read as "open") or a tracked file's row.
|
|
15
|
+
*
|
|
16
|
+
* **Read with an elevated identity, decided for the caller.** The permission rows are read as the MJ system user
|
|
17
|
+
* (resolved through `WellKnownUserSource`, the same way server-side `BaseEngine`s load). Reading them as the caller
|
|
18
|
+
* would turn "this user may not read the permission rows" into "this account has no rows", which the zero-rows rule
|
|
19
|
+
* below treats as open. When no elevated identity can be resolved, or any read fails, the answer is "no access"
|
|
20
|
+
* (fail closed).
|
|
21
|
+
*
|
|
22
|
+
* **The tracked-file rule.** An object that backs an `MJ: Files` row may be read, signed, listed, returned by search,
|
|
23
|
+
* overwritten, moved, copied or deleted only when the caller can read that row ({@link StorageAccessEvaluator.UnreadableTrackedObjectKeys}).
|
|
24
|
+
* Keys are compared through the account driver's `NormalizeObjectKey` and case-insensitively; a key whose meaning
|
|
25
|
+
* depends on the provider (`..`, `\`, `%2F`, ...) is refused outright (`IsSafeStorageObjectKey`).
|
|
26
|
+
*
|
|
27
|
+
* @module @memberjunction/storage
|
|
28
|
+
*/
|
|
29
|
+
import { IMetadataProvider, UserInfo } from '@memberjunction/core';
|
|
30
|
+
import { BaseSingleton } from '@memberjunction/global';
|
|
31
|
+
import type { MJFileEntity, MJFileStorageAccountPermissionEntity } from '@memberjunction/core-entities';
|
|
32
|
+
import { StorageObjectKeyNormalizer } from './generic/ObjectKeys.js';
|
|
33
|
+
/** What a caller wants to do with a storage account: list/download/search (`Read`) or upload/delete/move/copy into it (`Write`). */
|
|
34
|
+
export type StorageAccountAccess = 'Read' | 'Write';
|
|
35
|
+
/** The fields of an `MJ: File Storage Account Permissions` row the access decision reads. */
|
|
36
|
+
export type StorageAccountPermissionRow = Pick<MJFileStorageAccountPermissionEntity, 'FileStorageAccountID' | 'Type' | 'UserID' | 'RoleID' | 'CanRead' | 'CanWrite'>;
|
|
37
|
+
/** The permission state of a set of accounts, as read by {@link StorageAccessEvaluator.LoadAccountPermissionState}. */
|
|
38
|
+
export interface StorageAccountPermissionState {
|
|
39
|
+
/** Normalized ({@link NormalizeUUID}) IDs of the requested accounts that exist. */
|
|
40
|
+
ExistingAccountIDs: Set<string>;
|
|
41
|
+
/** Every permission row for the requested accounts. */
|
|
42
|
+
Rows: StorageAccountPermissionRow[];
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The refusal for a storage account the caller may not use. Deliberately the same for an account that does not exist, so a
|
|
46
|
+
* refusal never reveals whether an account ID is real.
|
|
47
|
+
*/
|
|
48
|
+
export declare const STORAGE_ACCOUNT_ACCESS_DENIED_MESSAGE = "You do not have access to this storage account or it does not exist.";
|
|
49
|
+
/** The refusal for an object backed by an `MJ: Files` row the caller may not read — the wording `GetFileContents` uses. */
|
|
50
|
+
export declare const TRACKED_FILE_ACCESS_DENIED_MESSAGE = "You do not have access to this file or it does not exist.";
|
|
51
|
+
/** Thrown by {@link StorageAccessEvaluator.AssertAccountAccess} when the caller may not use a storage account. */
|
|
52
|
+
export declare class StorageAccountAccessDeniedError extends Error {
|
|
53
|
+
constructor(message?: string);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Thrown by {@link StorageAccessEvaluator.AssertTrackedObjectsReadable} when an object backs an `MJ: Files` row the caller
|
|
57
|
+
* may not read (or its key is one the checks refuse — see {@link IsSafeStorageObjectKey}). Carries
|
|
58
|
+
* {@link TRACKED_FILE_ACCESS_DENIED_MESSAGE}, the wording `GetFileContents` uses for a row it cannot load.
|
|
59
|
+
*/
|
|
60
|
+
export declare class TrackedFileAccessDeniedError extends Error {
|
|
61
|
+
constructor(message?: string);
|
|
62
|
+
}
|
|
63
|
+
/** The `MJ: Files` columns needed to work out which object key a row backs. */
|
|
64
|
+
type TrackedFileRow = Pick<MJFileEntity, 'ID' | 'Name' | 'ProviderKey'>;
|
|
65
|
+
/**
|
|
66
|
+
* Decides storage-account access (`Read` / `Write`) and tracked-file read access for a user, per call. See the module
|
|
67
|
+
* documentation for the evaluation model.
|
|
68
|
+
*
|
|
69
|
+
* ```typescript
|
|
70
|
+
* const evaluator = StorageAccessEvaluator.Instance;
|
|
71
|
+
* await evaluator.AssertAccountAccess(accountId, contextUser, 'Write', provider); // throws when refused
|
|
72
|
+
* const readable = await evaluator.AccessibleAccountIDs(ids, contextUser, 'Read', provider);
|
|
73
|
+
* if (readable.has(NormalizeUUID(id))) { ... }
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export declare class StorageAccessEvaluator extends BaseSingleton<StorageAccessEvaluator> {
|
|
77
|
+
/**
|
|
78
|
+
* **The current product rule: a storage account with NO `MJ: File Storage Account Permissions` rows is open to every
|
|
79
|
+
* authenticated user, for both Read and Write.** The admin UI documents it ("If no roles are selected, all authorized
|
|
80
|
+
* users have access"), and every account created before account permissions existed relies on it. Entity-level
|
|
81
|
+
* permissions on `MJ: Files` still apply on top.
|
|
82
|
+
*
|
|
83
|
+
* This is the ONLY place the rule lives. Flipping it to "no rows means closed" is a product decision that needs a data
|
|
84
|
+
* migration writing an explicit `Everyone` row (CanRead, and CanWrite where wanted) for every existing account without
|
|
85
|
+
* rows — otherwise every such account goes dark at deploy time.
|
|
86
|
+
*/
|
|
87
|
+
private static readonly ACCOUNT_WITHOUT_PERMISSION_ROWS_IS_OPEN;
|
|
88
|
+
/** Returns the global singleton instance. */
|
|
89
|
+
static get Instance(): StorageAccessEvaluator;
|
|
90
|
+
/**
|
|
91
|
+
* The subset of `accountIDs` that `user` may use for `access`, as normalized ({@link NormalizeUUID}) IDs — compare with
|
|
92
|
+
* `NormalizeUUID(id)`. An ID that is not a UUID, names no account, or names an account the user may not use is left
|
|
93
|
+
* out; so is everything when the user cannot be evaluated or the permission read fails (fail closed).
|
|
94
|
+
*
|
|
95
|
+
* @param accountIDs - `MJ: File Storage Accounts` IDs to ask about (duplicates and casing are ignored)
|
|
96
|
+
* @param user - The user the decision is for
|
|
97
|
+
* @param access - `Read` checks `CanRead`; `Write` checks `CanWrite`
|
|
98
|
+
* @param provider - The metadata provider to read through; defaults to `Metadata.Provider`
|
|
99
|
+
*/
|
|
100
|
+
AccessibleAccountIDs(accountIDs: string[], user: UserInfo, access: StorageAccountAccess, provider?: IMetadataProvider): Promise<Set<string>>;
|
|
101
|
+
/** Whether `user` may use storage account `accountID` for `access`. See {@link AccessibleAccountIDs}. */
|
|
102
|
+
UserCanAccessAccount(accountID: string, user: UserInfo, access: StorageAccountAccess, provider?: IMetadataProvider): Promise<boolean>;
|
|
103
|
+
/**
|
|
104
|
+
* Throws {@link StorageAccountAccessDeniedError} unless `user` may use storage account `accountID` for `access`. The
|
|
105
|
+
* error carries {@link STORAGE_ACCOUNT_ACCESS_DENIED_MESSAGE} whether the account is restricted or does not exist.
|
|
106
|
+
*/
|
|
107
|
+
AssertAccountAccess(accountID: string, user: UserInfo, access: StorageAccountAccess, provider?: IMetadataProvider): Promise<void>;
|
|
108
|
+
/**
|
|
109
|
+
* The decision for ONE account, given all of its permission rows: open when it has none (the current product rule —
|
|
110
|
+
* see {@link ACCOUNT_WITHOUT_PERMISSION_ROWS_IS_OPEN}), otherwise granted when any row both allows `access`
|
|
111
|
+
* (`CanRead` / `CanWrite`) and applies to `user` (an `Everyone` row, a `Role` row for one of `user.UserRoles`, or a
|
|
112
|
+
* `User` row naming `user.ID`). A user that cannot be evaluated (no ID, or no `UserRoles` array) is refused.
|
|
113
|
+
*/
|
|
114
|
+
GrantsAccess(rows: StorageAccountPermissionRow[], user: UserInfo, access: StorageAccountAccess): boolean;
|
|
115
|
+
/**
|
|
116
|
+
* Reads, with the elevated reader, which of `accountIDs` exist and every permission row they have. Returns `null` when
|
|
117
|
+
* the question could not be asked (no elevated reader, or a failed read) — callers fail closed.
|
|
118
|
+
* Protected so a host with its own permission store can supply the state; `accountIDs` arrive validated and normalized.
|
|
119
|
+
*/
|
|
120
|
+
protected LoadAccountPermissionState(accountIDs: string[], provider?: IMetadataProvider): Promise<StorageAccountPermissionState | null>;
|
|
121
|
+
/**
|
|
122
|
+
* The identity the permission rows and the `MJ: Files` mapping are read with: the MJ system user for `provider`'s
|
|
123
|
+
* connection, or `null` when this process has none. Protected so a host can supply a different elevated reader.
|
|
124
|
+
*/
|
|
125
|
+
protected ResolveElevatedReader(provider: IMetadataProvider): Promise<UserInfo | null>;
|
|
126
|
+
/**
|
|
127
|
+
* Which of `objectKeys` back an `MJ: Files` row that `user` may not read. An object is tracked when an `MJ: Files` row
|
|
128
|
+
* on the account's storage provider resolves to it the way the server resolves every row to an object — `ProviderKey`,
|
|
129
|
+
* or `Name` when `ProviderKey` is empty. The mapping is read with the elevated reader (so a row the user cannot see
|
|
130
|
+
* still counts as tracked); whether the user may read each row is then asked as the user, through `RunView` (entity
|
|
131
|
+
* permission and row-level security both apply, as they do for `Load`). Reads are batched and never truncated.
|
|
132
|
+
*
|
|
133
|
+
* **Matching.** The key a caller sends and the key a row stores are both put through `keyNormalizer` — pass the
|
|
134
|
+
* account's driver, whose `NormalizeObjectKey` knows how that driver spells one object several ways (S3's key prefix;
|
|
135
|
+
* leading, trailing and repeated `/` everywhere) — and compared case-insensitively. The lookup finds rows whose stored
|
|
136
|
+
* key is the canonical key with or without a leading or trailing `/`, or the caller's own spelling; a row stored in any
|
|
137
|
+
* other non-canonical spelling (a driver prefix written into `ProviderKey`, doubled slashes) is not found, and MJ's
|
|
138
|
+
* own writers store neither.
|
|
139
|
+
*
|
|
140
|
+
* **Refused keys.** A key {@link IsSafeStorageObjectKey} rejects (a `.`/`..` segment, a backslash, a
|
|
141
|
+
* meaning-changing percent encoding, a control character) is always returned: its meaning depends on the provider,
|
|
142
|
+
* so it is never compared.
|
|
143
|
+
*
|
|
144
|
+
* Untracked keys are never returned — they are governed by the account gate alone. `MJ: Files` rows are keyed by
|
|
145
|
+
* PROVIDER, not account, so a tracked row in another account on the same provider with the same key also counts: the
|
|
146
|
+
* error is always toward refusing. Fails closed — when the mapping cannot be read every key is returned, and when the
|
|
147
|
+
* user-side read fails every tracked key is.
|
|
148
|
+
*
|
|
149
|
+
* @param fileStorageProviderID - The `MJ: File Storage Providers` ID of the account the objects live in
|
|
150
|
+
* @param objectKeys - Object keys as the caller or the driver spells them (path and/or provider object ID)
|
|
151
|
+
* @param keyNormalizer - The account's canonicalizer (its driver); defaults to `NormalizeStorageObjectKey`
|
|
152
|
+
*/
|
|
153
|
+
UnreadableTrackedObjectKeys(fileStorageProviderID: string, objectKeys: string[], user: UserInfo, provider?: IMetadataProvider, keyNormalizer?: StorageObjectKeyNormalizer): Promise<Set<string>>;
|
|
154
|
+
/**
|
|
155
|
+
* Throws {@link TrackedFileAccessDeniedError} when any of `objectKeys` backs an `MJ: Files` row `user` may not read
|
|
156
|
+
* (or is a key the checks refuse). See {@link UnreadableTrackedObjectKeys} for the rule.
|
|
157
|
+
*/
|
|
158
|
+
AssertTrackedObjectsReadable(fileStorageProviderID: string, objectKeys: string[], user: UserInfo, provider?: IMetadataProvider, keyNormalizer?: StorageObjectKeyNormalizer): Promise<void>;
|
|
159
|
+
/**
|
|
160
|
+
* Reads, with the elevated reader, the `MJ: Files` rows on `fileStorageProviderID` whose stored object key
|
|
161
|
+
* ({@link StorageObjectKeyOf}: `ProviderKey`, else `Name`), lower-cased, is one of `lookupValues` (already lower-cased). Batched, never truncated.
|
|
162
|
+
* Returns `null` when the read could not be made. Protected so a host can change the mapping.
|
|
163
|
+
*/
|
|
164
|
+
protected FindTrackedFiles(fileStorageProviderID: string, lookupValues: string[], provider: IMetadataProvider): Promise<TrackedFileRow[] | null>;
|
|
165
|
+
/** Whether a single permission row allows `access` and applies to `user`. */
|
|
166
|
+
private rowGrants;
|
|
167
|
+
/** A user can be evaluated only with an ID and a hydrated `UserRoles` array; anything less is refused. */
|
|
168
|
+
private canEvaluate;
|
|
169
|
+
/** Distinct, normalized, UUID-shaped IDs — anything else can name no account and is simply not asked about. */
|
|
170
|
+
private distinctValidAccountIDs;
|
|
171
|
+
/** {@link LoadAccountPermissionState}, with a throw turned into `null` (fail closed). */
|
|
172
|
+
private loadStateSafely;
|
|
173
|
+
/** Permission rows keyed by normalized account ID. */
|
|
174
|
+
private groupRowsByAccount;
|
|
175
|
+
/** The normalized account IDs in an accounts result. */
|
|
176
|
+
private accountIDsOf;
|
|
177
|
+
/** Narrows a row from the batched read to a permission row. */
|
|
178
|
+
private isPermissionRow;
|
|
179
|
+
/** Each safe key paired with its lower-cased canonical form; keys that canonicalize to the root (`''`) name no object. */
|
|
180
|
+
private canonicalKeys;
|
|
181
|
+
/**
|
|
182
|
+
* The lower-cased stored spellings the lookup matches for each key: the canonical key bare, with a leading `/`, with a
|
|
183
|
+
* trailing `/`, with both — and the caller's own spelling, so a row stored exactly as the caller spells it is found.
|
|
184
|
+
*/
|
|
185
|
+
private lookupValues;
|
|
186
|
+
/** `items` in consecutive batches of {@link IN_LIST_BATCH_SIZE}. */
|
|
187
|
+
private batches;
|
|
188
|
+
/** The keys (as the caller spelled them) whose tracked rows include one the user may not read. */
|
|
189
|
+
private keysBehindUnreadableRows;
|
|
190
|
+
/** The canonical, lower-cased object key the server resolves an `MJ: Files` row to ({@link StorageObjectKeyOf}). */
|
|
191
|
+
private trackedKeyOf;
|
|
192
|
+
/** Which of `fileIDs` `user` may read, asked as the user (batched, never truncated); `null` when a read fails. */
|
|
193
|
+
private readableFileIDs;
|
|
194
|
+
}
|
|
195
|
+
export {};
|
|
196
|
+
//# sourceMappingURL=StorageAccessEvaluator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StorageAccessEvaluator.d.ts","sourceRoot":"","sources":["../src/StorageAccessEvaluator.ts"],"names":[],"mappings":"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"}
|