@memberjunction/storage 6.2.0-edge.4 → 6.2.0-edge.6

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 (41) hide show
  1. package/dist/FileStorageEngine.d.ts +45 -1
  2. package/dist/FileStorageEngine.d.ts.map +1 -1
  3. package/dist/FileStorageEngine.js +72 -4
  4. package/dist/FileStorageEngine.js.map +1 -1
  5. package/dist/StorageAccessEvaluator.d.ts +196 -0
  6. package/dist/StorageAccessEvaluator.d.ts.map +1 -0
  7. package/dist/StorageAccessEvaluator.js +435 -0
  8. package/dist/StorageAccessEvaluator.js.map +1 -0
  9. package/dist/config.d.ts +22 -10
  10. package/dist/config.d.ts.map +1 -1
  11. package/dist/config.js +3 -0
  12. package/dist/config.js.map +1 -1
  13. package/dist/drivers/AWSFileStorage.d.ts +8 -0
  14. package/dist/drivers/AWSFileStorage.d.ts.map +1 -1
  15. package/dist/drivers/AWSFileStorage.js +11 -0
  16. package/dist/drivers/AWSFileStorage.js.map +1 -1
  17. package/dist/drivers/AzureFileStorage.d.ts +9 -0
  18. package/dist/drivers/AzureFileStorage.d.ts.map +1 -1
  19. package/dist/drivers/AzureFileStorage.js +22 -24
  20. package/dist/drivers/AzureFileStorage.js.map +1 -1
  21. package/dist/drivers/DropboxFileStorage.d.ts.map +1 -1
  22. package/dist/drivers/DropboxFileStorage.js +0 -51
  23. package/dist/drivers/DropboxFileStorage.js.map +1 -1
  24. package/dist/drivers/GoogleDriveFileStorage.d.ts +49 -2
  25. package/dist/drivers/GoogleDriveFileStorage.d.ts.map +1 -1
  26. package/dist/drivers/GoogleDriveFileStorage.js +80 -16
  27. package/dist/drivers/GoogleDriveFileStorage.js.map +1 -1
  28. package/dist/generic/FileStorageBase.d.ts +17 -1
  29. package/dist/generic/FileStorageBase.d.ts.map +1 -1
  30. package/dist/generic/FileStorageBase.js +18 -0
  31. package/dist/generic/FileStorageBase.js.map +1 -1
  32. package/dist/generic/ObjectKeys.d.ts +61 -0
  33. package/dist/generic/ObjectKeys.d.ts.map +1 -0
  34. package/dist/generic/ObjectKeys.js +82 -0
  35. package/dist/generic/ObjectKeys.js.map +1 -0
  36. package/dist/index.d.ts +2 -0
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +2 -0
  39. package/dist/index.js.map +1 -1
  40. package/package.json +6 -6
  41. package/readme.md +74 -0
@@ -0,0 +1,435 @@
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 { LogError, Metadata, RunView, WellKnownUserSource } from '@memberjunction/core';
30
+ import { BaseSingleton, EscapeSQLString, IsValidUUID, NormalizeUUID, UUIDsEqual } from '@memberjunction/global';
31
+ import { DEFAULT_OBJECT_KEY_NORMALIZER, IsSafeStorageObjectKey, StorageObjectKeyOf } from './generic/ObjectKeys.js';
32
+ /**
33
+ * The refusal for a storage account the caller may not use. Deliberately the same for an account that does not exist, so a
34
+ * refusal never reveals whether an account ID is real.
35
+ */
36
+ export const STORAGE_ACCOUNT_ACCESS_DENIED_MESSAGE = 'You do not have access to this storage account or it does not exist.';
37
+ /** The refusal for an object backed by an `MJ: Files` row the caller may not read — the wording `GetFileContents` uses. */
38
+ export const TRACKED_FILE_ACCESS_DENIED_MESSAGE = 'You do not have access to this file or it does not exist.';
39
+ /** Thrown by {@link StorageAccessEvaluator.AssertAccountAccess} when the caller may not use a storage account. */
40
+ export class StorageAccountAccessDeniedError extends Error {
41
+ constructor(message = STORAGE_ACCOUNT_ACCESS_DENIED_MESSAGE) {
42
+ super(message);
43
+ this.name = 'StorageAccountAccessDeniedError';
44
+ }
45
+ }
46
+ /**
47
+ * Thrown by {@link StorageAccessEvaluator.AssertTrackedObjectsReadable} when an object backs an `MJ: Files` row the caller
48
+ * may not read (or its key is one the checks refuse — see {@link IsSafeStorageObjectKey}). Carries
49
+ * {@link TRACKED_FILE_ACCESS_DENIED_MESSAGE}, the wording `GetFileContents` uses for a row it cannot load.
50
+ */
51
+ export class TrackedFileAccessDeniedError extends Error {
52
+ constructor(message = TRACKED_FILE_ACCESS_DENIED_MESSAGE) {
53
+ super(message);
54
+ this.name = 'TrackedFileAccessDeniedError';
55
+ }
56
+ }
57
+ /**
58
+ * How many values one `IN (...)` list carries. The tracked-file lookups are batched into lists of this size so a long
59
+ * listing or search page never becomes one oversized statement.
60
+ */
61
+ const IN_LIST_BATCH_SIZE = 500;
62
+ /**
63
+ * Decides storage-account access (`Read` / `Write`) and tracked-file read access for a user, per call. See the module
64
+ * documentation for the evaluation model.
65
+ *
66
+ * ```typescript
67
+ * const evaluator = StorageAccessEvaluator.Instance;
68
+ * await evaluator.AssertAccountAccess(accountId, contextUser, 'Write', provider); // throws when refused
69
+ * const readable = await evaluator.AccessibleAccountIDs(ids, contextUser, 'Read', provider);
70
+ * if (readable.has(NormalizeUUID(id))) { ... }
71
+ * ```
72
+ */
73
+ export class StorageAccessEvaluator extends BaseSingleton {
74
+ /**
75
+ * **The current product rule: a storage account with NO `MJ: File Storage Account Permissions` rows is open to every
76
+ * authenticated user, for both Read and Write.** The admin UI documents it ("If no roles are selected, all authorized
77
+ * users have access"), and every account created before account permissions existed relies on it. Entity-level
78
+ * permissions on `MJ: Files` still apply on top.
79
+ *
80
+ * This is the ONLY place the rule lives. Flipping it to "no rows means closed" is a product decision that needs a data
81
+ * migration writing an explicit `Everyone` row (CanRead, and CanWrite where wanted) for every existing account without
82
+ * rows — otherwise every such account goes dark at deploy time.
83
+ */
84
+ static { this.ACCOUNT_WITHOUT_PERMISSION_ROWS_IS_OPEN = true; }
85
+ /** Returns the global singleton instance. */
86
+ static get Instance() {
87
+ return super.getInstance();
88
+ }
89
+ // ─────────────────────────────────────────────────────────────────────
90
+ // Account access
91
+ // ─────────────────────────────────────────────────────────────────────
92
+ /**
93
+ * The subset of `accountIDs` that `user` may use for `access`, as normalized ({@link NormalizeUUID}) IDs — compare with
94
+ * `NormalizeUUID(id)`. An ID that is not a UUID, names no account, or names an account the user may not use is left
95
+ * out; so is everything when the user cannot be evaluated or the permission read fails (fail closed).
96
+ *
97
+ * @param accountIDs - `MJ: File Storage Accounts` IDs to ask about (duplicates and casing are ignored)
98
+ * @param user - The user the decision is for
99
+ * @param access - `Read` checks `CanRead`; `Write` checks `CanWrite`
100
+ * @param provider - The metadata provider to read through; defaults to `Metadata.Provider`
101
+ */
102
+ async AccessibleAccountIDs(accountIDs, user, access, provider) {
103
+ const allowed = new Set();
104
+ const requested = this.distinctValidAccountIDs(accountIDs);
105
+ if (requested.length === 0 || !this.canEvaluate(user)) {
106
+ return allowed;
107
+ }
108
+ const state = await this.loadStateSafely(requested, provider);
109
+ if (!state) {
110
+ return allowed; // fail closed — the failure is already logged
111
+ }
112
+ const rowsByAccount = this.groupRowsByAccount(state.Rows);
113
+ for (const id of requested) {
114
+ if (state.ExistingAccountIDs.has(id) && this.GrantsAccess(rowsByAccount.get(id) ?? [], user, access)) {
115
+ allowed.add(id);
116
+ }
117
+ }
118
+ return allowed;
119
+ }
120
+ /** Whether `user` may use storage account `accountID` for `access`. See {@link AccessibleAccountIDs}. */
121
+ async UserCanAccessAccount(accountID, user, access, provider) {
122
+ const allowed = await this.AccessibleAccountIDs([accountID], user, access, provider);
123
+ return allowed.has(NormalizeUUID(accountID));
124
+ }
125
+ /**
126
+ * Throws {@link StorageAccountAccessDeniedError} unless `user` may use storage account `accountID` for `access`. The
127
+ * error carries {@link STORAGE_ACCOUNT_ACCESS_DENIED_MESSAGE} whether the account is restricted or does not exist.
128
+ */
129
+ async AssertAccountAccess(accountID, user, access, provider) {
130
+ if (!(await this.UserCanAccessAccount(accountID, user, access, provider))) {
131
+ throw new StorageAccountAccessDeniedError();
132
+ }
133
+ }
134
+ /**
135
+ * The decision for ONE account, given all of its permission rows: open when it has none (the current product rule —
136
+ * see {@link ACCOUNT_WITHOUT_PERMISSION_ROWS_IS_OPEN}), otherwise granted when any row both allows `access`
137
+ * (`CanRead` / `CanWrite`) and applies to `user` (an `Everyone` row, a `Role` row for one of `user.UserRoles`, or a
138
+ * `User` row naming `user.ID`). A user that cannot be evaluated (no ID, or no `UserRoles` array) is refused.
139
+ */
140
+ GrantsAccess(rows, user, access) {
141
+ if (!this.canEvaluate(user)) {
142
+ return false;
143
+ }
144
+ if (rows.length === 0) {
145
+ return StorageAccessEvaluator.ACCOUNT_WITHOUT_PERMISSION_ROWS_IS_OPEN;
146
+ }
147
+ const roleIDs = new Set(user.UserRoles.map(r => NormalizeUUID(r.RoleID)));
148
+ return rows.some(row => this.rowGrants(row, user, roleIDs, access));
149
+ }
150
+ /**
151
+ * Reads, with the elevated reader, which of `accountIDs` exist and every permission row they have. Returns `null` when
152
+ * the question could not be asked (no elevated reader, or a failed read) — callers fail closed.
153
+ * Protected so a host with its own permission store can supply the state; `accountIDs` arrive validated and normalized.
154
+ */
155
+ async LoadAccountPermissionState(accountIDs, provider) {
156
+ const md = provider ?? Metadata.Provider;
157
+ const reader = await this.ResolveElevatedReader(md);
158
+ if (!reader) {
159
+ LogError('StorageAccessEvaluator: no elevated reader (MJ system user) could be resolved — storage access denied');
160
+ return null;
161
+ }
162
+ const inList = accountIDs.map(id => `'${EscapeSQLString(id)}'`).join(', ');
163
+ const [accounts, permissions] = await RunView.FromMetadataProvider(md).RunViews([
164
+ // IgnoreMaxRows on both: a truncated read would drop rows, and an account whose rows were all dropped would look
165
+ // like an account with none — which the zero-rows rule treats as open.
166
+ { EntityName: 'MJ: File Storage Accounts', ExtraFilter: `ID IN (${inList})`, Fields: ['ID'], ResultType: 'simple', IgnoreMaxRows: true },
167
+ {
168
+ EntityName: 'MJ: File Storage Account Permissions',
169
+ ExtraFilter: `FileStorageAccountID IN (${inList})`,
170
+ Fields: ['FileStorageAccountID', 'Type', 'UserID', 'RoleID', 'CanRead', 'CanWrite'],
171
+ ResultType: 'simple',
172
+ IgnoreMaxRows: true
173
+ }
174
+ ], reader);
175
+ if (!accounts?.Success || !permissions?.Success) {
176
+ LogError(`StorageAccessEvaluator: permission read failed: ${accounts?.ErrorMessage ?? permissions?.ErrorMessage ?? 'no result'}`);
177
+ return null;
178
+ }
179
+ const rows = permissions.Results.filter((r) => this.isPermissionRow(r));
180
+ return { ExistingAccountIDs: this.accountIDsOf(accounts), Rows: rows };
181
+ }
182
+ /**
183
+ * The identity the permission rows and the `MJ: Files` mapping are read with: the MJ system user for `provider`'s
184
+ * connection, or `null` when this process has none. Protected so a host can supply a different elevated reader.
185
+ */
186
+ async ResolveElevatedReader(provider) {
187
+ try {
188
+ return await WellKnownUserSource.Instance.GetSystemUser(provider);
189
+ }
190
+ catch (error) {
191
+ LogError(`StorageAccessEvaluator: resolving the elevated reader failed: ${error instanceof Error ? error.message : String(error)}`);
192
+ return null;
193
+ }
194
+ }
195
+ // ─────────────────────────────────────────────────────────────────────
196
+ // Tracked files
197
+ // ─────────────────────────────────────────────────────────────────────
198
+ /**
199
+ * Which of `objectKeys` back an `MJ: Files` row that `user` may not read. An object is tracked when an `MJ: Files` row
200
+ * on the account's storage provider resolves to it the way the server resolves every row to an object — `ProviderKey`,
201
+ * or `Name` when `ProviderKey` is empty. The mapping is read with the elevated reader (so a row the user cannot see
202
+ * still counts as tracked); whether the user may read each row is then asked as the user, through `RunView` (entity
203
+ * permission and row-level security both apply, as they do for `Load`). Reads are batched and never truncated.
204
+ *
205
+ * **Matching.** The key a caller sends and the key a row stores are both put through `keyNormalizer` — pass the
206
+ * account's driver, whose `NormalizeObjectKey` knows how that driver spells one object several ways (S3's key prefix;
207
+ * leading, trailing and repeated `/` everywhere) — and compared case-insensitively. The lookup finds rows whose stored
208
+ * key is the canonical key with or without a leading or trailing `/`, or the caller's own spelling; a row stored in any
209
+ * other non-canonical spelling (a driver prefix written into `ProviderKey`, doubled slashes) is not found, and MJ's
210
+ * own writers store neither.
211
+ *
212
+ * **Refused keys.** A key {@link IsSafeStorageObjectKey} rejects (a `.`/`..` segment, a backslash, a
213
+ * meaning-changing percent encoding, a control character) is always returned: its meaning depends on the provider,
214
+ * so it is never compared.
215
+ *
216
+ * Untracked keys are never returned — they are governed by the account gate alone. `MJ: Files` rows are keyed by
217
+ * PROVIDER, not account, so a tracked row in another account on the same provider with the same key also counts: the
218
+ * error is always toward refusing. Fails closed — when the mapping cannot be read every key is returned, and when the
219
+ * user-side read fails every tracked key is.
220
+ *
221
+ * @param fileStorageProviderID - The `MJ: File Storage Providers` ID of the account the objects live in
222
+ * @param objectKeys - Object keys as the caller or the driver spells them (path and/or provider object ID)
223
+ * @param keyNormalizer - The account's canonicalizer (its driver); defaults to `NormalizeStorageObjectKey`
224
+ */
225
+ async UnreadableTrackedObjectKeys(fileStorageProviderID, objectKeys, user, provider, keyNormalizer = DEFAULT_OBJECT_KEY_NORMALIZER) {
226
+ const keys = Array.from(new Set((objectKeys ?? []).filter(k => typeof k === 'string' && k.length > 0)));
227
+ const refused = keys.filter(k => !IsSafeStorageObjectKey(k));
228
+ try {
229
+ const canonical = this.canonicalKeys(keys.filter(k => IsSafeStorageObjectKey(k)), keyNormalizer);
230
+ if (canonical.size === 0) {
231
+ return new Set(refused);
232
+ }
233
+ const md = provider ?? Metadata.Provider;
234
+ const tracked = await this.FindTrackedFiles(fileStorageProviderID, this.lookupValues(canonical), md);
235
+ if (!tracked) {
236
+ return new Set(keys);
237
+ }
238
+ const unreadable = await this.keysBehindUnreadableRows(canonical, tracked, keyNormalizer, user, md);
239
+ return new Set([...refused, ...unreadable]);
240
+ }
241
+ catch (error) {
242
+ LogError(`StorageAccessEvaluator: tracked-file check failed: ${error instanceof Error ? error.message : String(error)}`);
243
+ return new Set(keys);
244
+ }
245
+ }
246
+ /**
247
+ * Throws {@link TrackedFileAccessDeniedError} when any of `objectKeys` backs an `MJ: Files` row `user` may not read
248
+ * (or is a key the checks refuse). See {@link UnreadableTrackedObjectKeys} for the rule.
249
+ */
250
+ async AssertTrackedObjectsReadable(fileStorageProviderID, objectKeys, user, provider, keyNormalizer = DEFAULT_OBJECT_KEY_NORMALIZER) {
251
+ const unreadable = await this.UnreadableTrackedObjectKeys(fileStorageProviderID, objectKeys, user, provider, keyNormalizer);
252
+ if (unreadable.size > 0) {
253
+ throw new TrackedFileAccessDeniedError();
254
+ }
255
+ }
256
+ /**
257
+ * Reads, with the elevated reader, the `MJ: Files` rows on `fileStorageProviderID` whose stored object key
258
+ * ({@link StorageObjectKeyOf}: `ProviderKey`, else `Name`), lower-cased, is one of `lookupValues` (already lower-cased). Batched, never truncated.
259
+ * Returns `null` when the read could not be made. Protected so a host can change the mapping.
260
+ */
261
+ async FindTrackedFiles(fileStorageProviderID, lookupValues, provider) {
262
+ if (!IsValidUUID(fileStorageProviderID)) {
263
+ return null;
264
+ }
265
+ const reader = await this.ResolveElevatedReader(provider);
266
+ if (!reader) {
267
+ LogError('StorageAccessEvaluator: no elevated reader (MJ system user) could be resolved — tracked-file check denied');
268
+ return null;
269
+ }
270
+ const providerFilter = `ProviderID = '${EscapeSQLString(fileStorageProviderID)}'`;
271
+ const views = this.batches(lookupValues).map(batch => {
272
+ const inList = batch.map(v => `'${EscapeSQLString(v)}'`).join(', ');
273
+ return {
274
+ EntityName: 'MJ: Files',
275
+ ExtraFilter: `${providerFilter} AND (LOWER(ProviderKey) IN (${inList}) OR ((ProviderKey IS NULL OR ProviderKey = '') AND LOWER(Name) IN (${inList})))`,
276
+ Fields: ['ID', 'Name', 'ProviderKey'],
277
+ ResultType: 'simple',
278
+ IgnoreMaxRows: true
279
+ };
280
+ });
281
+ const results = await RunView.FromMetadataProvider(provider).RunViews(views, reader);
282
+ const failed = results.find(r => !r?.Success);
283
+ if (failed || results.length !== views.length) {
284
+ LogError(`StorageAccessEvaluator: tracked-file lookup failed: ${failed?.ErrorMessage ?? 'no result'}`);
285
+ return null;
286
+ }
287
+ return results.flatMap(r => r.Results);
288
+ }
289
+ // ─────────────────────────────────────────────────────────────────────
290
+ // Helpers
291
+ // ─────────────────────────────────────────────────────────────────────
292
+ /** Whether a single permission row allows `access` and applies to `user`. */
293
+ rowGrants(row, user, roleIDs, access) {
294
+ const allows = access === 'Write' ? row.CanWrite : row.CanRead;
295
+ if (!allows) {
296
+ return false;
297
+ }
298
+ switch (row.Type) {
299
+ case 'Everyone':
300
+ return true;
301
+ case 'User':
302
+ return row.UserID != null && UUIDsEqual(row.UserID, user.ID);
303
+ case 'Role':
304
+ return row.RoleID != null && roleIDs.has(NormalizeUUID(row.RoleID));
305
+ default:
306
+ return false;
307
+ }
308
+ }
309
+ /** A user can be evaluated only with an ID and a hydrated `UserRoles` array; anything less is refused. */
310
+ canEvaluate(user) {
311
+ const ok = user != null && typeof user.ID === 'string' && user.ID.length > 0 && Array.isArray(user.UserRoles);
312
+ if (!ok) {
313
+ LogError('StorageAccessEvaluator: the user cannot be evaluated (missing ID or UserRoles) — storage access denied');
314
+ }
315
+ return ok;
316
+ }
317
+ /** Distinct, normalized, UUID-shaped IDs — anything else can name no account and is simply not asked about. */
318
+ distinctValidAccountIDs(accountIDs) {
319
+ return Array.from(new Set((accountIDs ?? []).filter(id => IsValidUUID(id)).map(id => NormalizeUUID(id))));
320
+ }
321
+ /** {@link LoadAccountPermissionState}, with a throw turned into `null` (fail closed). */
322
+ async loadStateSafely(accountIDs, provider) {
323
+ try {
324
+ return await this.LoadAccountPermissionState(accountIDs, provider);
325
+ }
326
+ catch (error) {
327
+ LogError(`StorageAccessEvaluator: permission read threw: ${error instanceof Error ? error.message : String(error)}`);
328
+ return null;
329
+ }
330
+ }
331
+ /** Permission rows keyed by normalized account ID. */
332
+ groupRowsByAccount(rows) {
333
+ const byAccount = new Map();
334
+ for (const row of rows) {
335
+ const id = NormalizeUUID(row.FileStorageAccountID);
336
+ const list = byAccount.get(id);
337
+ if (list) {
338
+ list.push(row);
339
+ }
340
+ else {
341
+ byAccount.set(id, [row]);
342
+ }
343
+ }
344
+ return byAccount;
345
+ }
346
+ /** The normalized account IDs in an accounts result. */
347
+ accountIDsOf(result) {
348
+ const ids = new Set();
349
+ for (const row of result.Results) {
350
+ if ('ID' in row && row.ID) {
351
+ ids.add(NormalizeUUID(row.ID));
352
+ }
353
+ }
354
+ return ids;
355
+ }
356
+ /** Narrows a row from the batched read to a permission row. */
357
+ isPermissionRow(row) {
358
+ return 'FileStorageAccountID' in row;
359
+ }
360
+ /** Each safe key paired with its lower-cased canonical form; keys that canonicalize to the root (`''`) name no object. */
361
+ canonicalKeys(keys, keyNormalizer) {
362
+ const canonical = new Map();
363
+ for (const key of keys) {
364
+ const normalized = keyNormalizer.NormalizeObjectKey(key).toLowerCase();
365
+ if (normalized.length > 0) {
366
+ canonical.set(key, normalized);
367
+ }
368
+ }
369
+ return canonical;
370
+ }
371
+ /**
372
+ * The lower-cased stored spellings the lookup matches for each key: the canonical key bare, with a leading `/`, with a
373
+ * trailing `/`, with both — and the caller's own spelling, so a row stored exactly as the caller spells it is found.
374
+ */
375
+ lookupValues(canonical) {
376
+ const values = new Set();
377
+ for (const [raw, key] of canonical) {
378
+ [key, `/${key}`, `${key}/`, `/${key}/`, raw.trim().toLowerCase()].forEach(v => values.add(v));
379
+ }
380
+ return Array.from(values);
381
+ }
382
+ /** `items` in consecutive batches of {@link IN_LIST_BATCH_SIZE}. */
383
+ batches(items) {
384
+ const out = [];
385
+ for (let i = 0; i < items.length; i += IN_LIST_BATCH_SIZE) {
386
+ out.push(items.slice(i, i + IN_LIST_BATCH_SIZE));
387
+ }
388
+ return out;
389
+ }
390
+ /** The keys (as the caller spelled them) whose tracked rows include one the user may not read. */
391
+ async keysBehindUnreadableRows(canonical, tracked, keyNormalizer, user, provider) {
392
+ const unreadable = new Set();
393
+ const wanted = new Set(canonical.values());
394
+ const rowsByKey = new Map();
395
+ for (const row of tracked) {
396
+ const key = this.trackedKeyOf(row, keyNormalizer);
397
+ if (wanted.has(key)) {
398
+ rowsByKey.set(key, [...(rowsByKey.get(key) ?? []), row]);
399
+ }
400
+ }
401
+ if (rowsByKey.size === 0) {
402
+ return unreadable;
403
+ }
404
+ const rowIDs = new Set(Array.from(rowsByKey.values()).flat().map(f => f.ID));
405
+ const readable = await this.readableFileIDs(Array.from(rowIDs), user, provider);
406
+ for (const [raw, key] of canonical) {
407
+ if ((rowsByKey.get(key) ?? []).some(f => !readable || !readable.has(NormalizeUUID(f.ID)))) {
408
+ unreadable.add(raw);
409
+ }
410
+ }
411
+ return unreadable;
412
+ }
413
+ /** The canonical, lower-cased object key the server resolves an `MJ: Files` row to ({@link StorageObjectKeyOf}). */
414
+ trackedKeyOf(file, keyNormalizer) {
415
+ return keyNormalizer.NormalizeObjectKey(StorageObjectKeyOf(file)).toLowerCase();
416
+ }
417
+ /** Which of `fileIDs` `user` may read, asked as the user (batched, never truncated); `null` when a read fails. */
418
+ async readableFileIDs(fileIDs, user, provider) {
419
+ const views = this.batches(fileIDs).map(batch => ({
420
+ EntityName: 'MJ: Files',
421
+ ExtraFilter: `ID IN (${batch.map(id => `'${EscapeSQLString(id)}'`).join(', ')})`,
422
+ Fields: ['ID'],
423
+ ResultType: 'simple',
424
+ IgnoreMaxRows: true
425
+ }));
426
+ const results = await RunView.FromMetadataProvider(provider).RunViews(views, user);
427
+ const failed = results.find(r => !r?.Success);
428
+ if (failed || results.length !== views.length) {
429
+ LogError(`StorageAccessEvaluator: reading MJ: Files as the user failed: ${failed?.ErrorMessage ?? 'no result'}`);
430
+ return null;
431
+ }
432
+ return new Set(results.flatMap(r => r.Results).map(r => NormalizeUUID(r.ID)));
433
+ }
434
+ }
435
+ //# sourceMappingURL=StorageAccessEvaluator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StorageAccessEvaluator.js","sourceRoot":"","sources":["../src/StorageAccessEvaluator.ts"],"names":[],"mappings":"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"}
package/dist/config.d.ts CHANGED
@@ -79,6 +79,7 @@ declare const storageProvidersSchema: z.ZodObject<{
79
79
  refreshToken: z.ZodOptional<z.ZodString>;
80
80
  redirectURI: z.ZodOptional<z.ZodString>;
81
81
  rootFolderID: z.ZodOptional<z.ZodString>;
82
+ driveID: z.ZodOptional<z.ZodString>;
82
83
  }, "strip", z.ZodTypeAny, {
83
84
  refreshToken?: string;
84
85
  keyFile?: string;
@@ -87,6 +88,7 @@ declare const storageProvidersSchema: z.ZodObject<{
87
88
  clientSecret?: string;
88
89
  redirectURI?: string;
89
90
  rootFolderID?: string;
91
+ driveID?: string;
90
92
  }, {
91
93
  refreshToken?: string;
92
94
  keyFile?: string;
@@ -95,6 +97,7 @@ declare const storageProvidersSchema: z.ZodObject<{
95
97
  clientSecret?: string;
96
98
  redirectURI?: string;
97
99
  rootFolderID?: string;
100
+ driveID?: string;
98
101
  }>>;
99
102
  /**
100
103
  * Dropbox Configuration
@@ -161,16 +164,16 @@ declare const storageProvidersSchema: z.ZodObject<{
161
164
  clientID?: string;
162
165
  clientSecret?: string;
163
166
  rootFolderID?: string;
167
+ driveID?: string;
164
168
  tenantID?: string;
165
169
  siteID?: string;
166
- driveID?: string;
167
170
  }, {
168
171
  clientID?: string;
169
172
  clientSecret?: string;
170
173
  rootFolderID?: string;
174
+ driveID?: string;
171
175
  tenantID?: string;
172
176
  siteID?: string;
173
- driveID?: string;
174
177
  }>>;
175
178
  }, "strip", z.ZodTypeAny, {
176
179
  aws?: {
@@ -200,6 +203,7 @@ declare const storageProvidersSchema: z.ZodObject<{
200
203
  clientSecret?: string;
201
204
  redirectURI?: string;
202
205
  rootFolderID?: string;
206
+ driveID?: string;
203
207
  };
204
208
  dropbox?: {
205
209
  refreshToken?: string;
@@ -220,9 +224,9 @@ declare const storageProvidersSchema: z.ZodObject<{
220
224
  clientID?: string;
221
225
  clientSecret?: string;
222
226
  rootFolderID?: string;
227
+ driveID?: string;
223
228
  tenantID?: string;
224
229
  siteID?: string;
225
- driveID?: string;
226
230
  };
227
231
  }, {
228
232
  aws?: {
@@ -252,6 +256,7 @@ declare const storageProvidersSchema: z.ZodObject<{
252
256
  clientSecret?: string;
253
257
  redirectURI?: string;
254
258
  rootFolderID?: string;
259
+ driveID?: string;
255
260
  };
256
261
  dropbox?: {
257
262
  refreshToken?: string;
@@ -272,9 +277,9 @@ declare const storageProvidersSchema: z.ZodObject<{
272
277
  clientID?: string;
273
278
  clientSecret?: string;
274
279
  rootFolderID?: string;
280
+ driveID?: string;
275
281
  tenantID?: string;
276
282
  siteID?: string;
277
- driveID?: string;
278
283
  };
279
284
  }>;
280
285
  /**
@@ -361,6 +366,7 @@ declare const storageConfigSchema: z.ZodObject<{
361
366
  refreshToken: z.ZodOptional<z.ZodString>;
362
367
  redirectURI: z.ZodOptional<z.ZodString>;
363
368
  rootFolderID: z.ZodOptional<z.ZodString>;
369
+ driveID: z.ZodOptional<z.ZodString>;
364
370
  }, "strip", z.ZodTypeAny, {
365
371
  refreshToken?: string;
366
372
  keyFile?: string;
@@ -369,6 +375,7 @@ declare const storageConfigSchema: z.ZodObject<{
369
375
  clientSecret?: string;
370
376
  redirectURI?: string;
371
377
  rootFolderID?: string;
378
+ driveID?: string;
372
379
  }, {
373
380
  refreshToken?: string;
374
381
  keyFile?: string;
@@ -377,6 +384,7 @@ declare const storageConfigSchema: z.ZodObject<{
377
384
  clientSecret?: string;
378
385
  redirectURI?: string;
379
386
  rootFolderID?: string;
387
+ driveID?: string;
380
388
  }>>;
381
389
  /**
382
390
  * Dropbox Configuration
@@ -443,16 +451,16 @@ declare const storageConfigSchema: z.ZodObject<{
443
451
  clientID?: string;
444
452
  clientSecret?: string;
445
453
  rootFolderID?: string;
454
+ driveID?: string;
446
455
  tenantID?: string;
447
456
  siteID?: string;
448
- driveID?: string;
449
457
  }, {
450
458
  clientID?: string;
451
459
  clientSecret?: string;
452
460
  rootFolderID?: string;
461
+ driveID?: string;
453
462
  tenantID?: string;
454
463
  siteID?: string;
455
- driveID?: string;
456
464
  }>>;
457
465
  }, "strip", z.ZodTypeAny, {
458
466
  aws?: {
@@ -482,6 +490,7 @@ declare const storageConfigSchema: z.ZodObject<{
482
490
  clientSecret?: string;
483
491
  redirectURI?: string;
484
492
  rootFolderID?: string;
493
+ driveID?: string;
485
494
  };
486
495
  dropbox?: {
487
496
  refreshToken?: string;
@@ -502,9 +511,9 @@ declare const storageConfigSchema: z.ZodObject<{
502
511
  clientID?: string;
503
512
  clientSecret?: string;
504
513
  rootFolderID?: string;
514
+ driveID?: string;
505
515
  tenantID?: string;
506
516
  siteID?: string;
507
- driveID?: string;
508
517
  };
509
518
  }, {
510
519
  aws?: {
@@ -534,6 +543,7 @@ declare const storageConfigSchema: z.ZodObject<{
534
543
  clientSecret?: string;
535
544
  redirectURI?: string;
536
545
  rootFolderID?: string;
546
+ driveID?: string;
537
547
  };
538
548
  dropbox?: {
539
549
  refreshToken?: string;
@@ -554,9 +564,9 @@ declare const storageConfigSchema: z.ZodObject<{
554
564
  clientID?: string;
555
565
  clientSecret?: string;
556
566
  rootFolderID?: string;
567
+ driveID?: string;
557
568
  tenantID?: string;
558
569
  siteID?: string;
559
- driveID?: string;
560
570
  };
561
571
  }>>>;
562
572
  }, "strip", z.ZodTypeAny, {
@@ -588,6 +598,7 @@ declare const storageConfigSchema: z.ZodObject<{
588
598
  clientSecret?: string;
589
599
  redirectURI?: string;
590
600
  rootFolderID?: string;
601
+ driveID?: string;
591
602
  };
592
603
  dropbox?: {
593
604
  refreshToken?: string;
@@ -608,9 +619,9 @@ declare const storageConfigSchema: z.ZodObject<{
608
619
  clientID?: string;
609
620
  clientSecret?: string;
610
621
  rootFolderID?: string;
622
+ driveID?: string;
611
623
  tenantID?: string;
612
624
  siteID?: string;
613
- driveID?: string;
614
625
  };
615
626
  };
616
627
  }, {
@@ -642,6 +653,7 @@ declare const storageConfigSchema: z.ZodObject<{
642
653
  clientSecret?: string;
643
654
  redirectURI?: string;
644
655
  rootFolderID?: string;
656
+ driveID?: string;
645
657
  };
646
658
  dropbox?: {
647
659
  refreshToken?: string;
@@ -662,9 +674,9 @@ declare const storageConfigSchema: z.ZodObject<{
662
674
  clientID?: string;
663
675
  clientSecret?: string;
664
676
  rootFolderID?: string;
677
+ driveID?: string;
665
678
  tenantID?: string;
666
679
  siteID?: string;
667
- driveID?: string;
668
680
  };
669
681
  };
670
682
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB;;GAEG;AACH,QAAA,MAAM,sBAAsB;IAC1B;;;OAGG;;;;;;;;;;;;;;;;;;;;IAWH;;;OAGG;;;;;;;;;;;;;;;;;IAUH;;;OAGG;;;;;;;;;;;;;;;;;IAUH;;;;OAIG;;;;;;;;;;;;;;;;;;;;;;;;;;IAeH;;;OAGG;;;;;;;;;;;;;;;;;;;;IAWH;;;;OAIG;;;;;;;;;;;;;;;;;;;;;;;IAYH;;;OAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAWH,CAAC;AAEH;;GAEG;AACH,QAAA,MAAM,mBAAmB;IACvB;;OAEG;;QA/GH;;;WAGG;;;;;;;;;;;;;;;;;;;;QAWH;;;WAGG;;;;;;;;;;;;;;;;;QAUH;;;WAGG;;;;;;;;;;;;;;;;;QAUH;;;;WAIG;;;;;;;;;;;;;;;;;;;;;;;;;;QAeH;;;WAGG;;;;;;;;;;;;;;;;;;;;QAWH;;;;WAIG;;;;;;;;;;;;;;;;;;;;;;;QAYH;;;WAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqBH,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAChE,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAI5E;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,aAAa,CAgFhD;AAED,gDAAgD;AAChD,wBAAgB,gBAAgB,IAAI,aAAa,CAEhD;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,IAAI,sBAAsB,CAGlE;AAED,yDAAyD;AACzD,wBAAgB,yBAAyB,IAAI,sBAAsB,CAElE;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,MAAM,sBAAsB,EAAE,QAAQ,EAAE,CAAC,GAAG,sBAAsB,CAAC,CAAC,CAAC,CAGhH;AAED,iDAAiD;AACjD,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,MAAM,sBAAsB,EAAE,QAAQ,EAAE,CAAC,GAAG,sBAAsB,CAAC,CAAC,CAAC,CAEhH;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC;AAED,kDAAkD;AAClD,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB;;GAEG;AACH,QAAA,MAAM,sBAAsB;IAC1B;;;OAGG;;;;;;;;;;;;;;;;;;;;IAWH;;;OAGG;;;;;;;;;;;;;;;;;IAUH;;;OAGG;;;;;;;;;;;;;;;;;IAUH;;;;OAIG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAiBH;;;OAGG;;;;;;;;;;;;;;;;;;;;IAWH;;;;OAIG;;;;;;;;;;;;;;;;;;;;;;;IAYH;;;OAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAWH,CAAC;AAEH;;GAEG;AACH,QAAA,MAAM,mBAAmB;IACvB;;OAEG;;QAjHH;;;WAGG;;;;;;;;;;;;;;;;;;;;QAWH;;;WAGG;;;;;;;;;;;;;;;;;QAUH;;;WAGG;;;;;;;;;;;;;;;;;QAUH;;;;WAIG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAiBH;;;WAGG;;;;;;;;;;;;;;;;;;;;QAWH;;;;WAIG;;;;;;;;;;;;;;;;;;;;;;;QAYH;;;WAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqBH,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAChE,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAI5E;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,aAAa,CAiFhD;AAED,gDAAgD;AAChD,wBAAgB,gBAAgB,IAAI,aAAa,CAEhD;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,IAAI,sBAAsB,CAGlE;AAED,yDAAyD;AACzD,wBAAgB,yBAAyB,IAAI,sBAAsB,CAElE;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,MAAM,sBAAsB,EAAE,QAAQ,EAAE,CAAC,GAAG,sBAAsB,CAAC,CAAC,CAAC,CAGhH;AAED,iDAAiD;AACjD,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,MAAM,sBAAsB,EAAE,QAAQ,EAAE,CAAC,GAAG,sBAAsB,CAAC,CAAC,CAAC,CAEhH;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC;AAED,kDAAkD;AAClD,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC"}