@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.
Files changed (38) 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/GoogleDriveFileStorage.d.ts +49 -2
  22. package/dist/drivers/GoogleDriveFileStorage.d.ts.map +1 -1
  23. package/dist/drivers/GoogleDriveFileStorage.js +80 -16
  24. package/dist/drivers/GoogleDriveFileStorage.js.map +1 -1
  25. package/dist/generic/FileStorageBase.d.ts +17 -1
  26. package/dist/generic/FileStorageBase.d.ts.map +1 -1
  27. package/dist/generic/FileStorageBase.js +18 -0
  28. package/dist/generic/FileStorageBase.js.map +1 -1
  29. package/dist/generic/ObjectKeys.d.ts +61 -0
  30. package/dist/generic/ObjectKeys.d.ts.map +1 -0
  31. package/dist/generic/ObjectKeys.js +82 -0
  32. package/dist/generic/ObjectKeys.js.map +1 -0
  33. package/dist/index.d.ts +2 -0
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +2 -0
  36. package/dist/index.js.map +1 -1
  37. package/package.json +6 -6
  38. package/readme.md +74 -0
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,kCAAkC,CAAC;AACjD,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,0BAA0B,CAAC;AACzC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,QAAQ,CAAC;AACvB,cAAc,UAAU,CAAC;AACzB,cAAc,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,kCAAkC,CAAC;AACjD,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,0BAA0B,CAAC;AACzC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,sBAAsB,CAAC;AACrC,cAAc,QAAQ,CAAC;AACvB,cAAc,UAAU,CAAC;AACzB,cAAc,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC"}
package/dist/index.js CHANGED
@@ -6,7 +6,9 @@ export * from './drivers/SharePointFileStorage.js';
6
6
  export * from './drivers/DropboxFileStorage.js';
7
7
  export * from './drivers/BoxFileStorage.js';
8
8
  export * from './generic/FileStorageBase.js';
9
+ export * from './generic/ObjectKeys.js';
9
10
  export * from './util.js';
10
11
  export * from './config.js';
11
12
  export * from './FileStorageEngine.js';
13
+ export * from './StorageAccessEvaluator.js';
12
14
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,kCAAkC,CAAC;AACjD,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,0BAA0B,CAAC;AACzC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,QAAQ,CAAC;AACvB,cAAc,UAAU,CAAC;AACzB,cAAc,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,kCAAkC,CAAC;AACjD,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,0BAA0B,CAAC;AACzC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,sBAAsB,CAAC;AACrC,cAAc,QAAQ,CAAC;AACvB,cAAc,UAAU,CAAC;AACzB,cAAc,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@memberjunction/storage",
3
3
  "type": "module",
4
- "version": "6.2.0-edge.4",
4
+ "version": "6.2.0-edge.5",
5
5
  "description": "This library provides a set of objects that handle the interface between the server-side API and various cloud storage providers.",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -21,11 +21,11 @@
21
21
  "@azure/identity": "^4.13.0",
22
22
  "@azure/storage-blob": "^12.30.0",
23
23
  "@google-cloud/storage": "^7.19.0",
24
- "@memberjunction/core": "6.2.0-edge.4",
25
- "@memberjunction/core-entities": "6.2.0-edge.4",
26
- "@memberjunction/credentials": "6.2.0-edge.4",
27
- "@memberjunction/global": "6.2.0-edge.4",
28
- "@memberjunction/network-utils": "6.2.0-edge.4",
24
+ "@memberjunction/core": "6.2.0-edge.5",
25
+ "@memberjunction/core-entities": "6.2.0-edge.5",
26
+ "@memberjunction/credentials": "6.2.0-edge.5",
27
+ "@memberjunction/global": "6.2.0-edge.5",
28
+ "@memberjunction/network-utils": "6.2.0-edge.5",
29
29
  "@microsoft/mgt-element": "^4.6.0",
30
30
  "@microsoft/mgt-msal2-provider": "^4.6.0",
31
31
  "@microsoft/mgt-sharepoint-provider": "^4.6.0",
package/readme.md CHANGED
@@ -251,6 +251,79 @@ sequenceDiagram
251
251
  Driver-->>App: initialized driver
252
252
  ```
253
253
 
254
+ ### Account Permissions (`StorageAccessEvaluator`)
255
+
256
+ Neither `FileStorageEngine.GetDriver()` nor the utility functions check who is asking — they act on whatever account
257
+ they are given. Anything that acts on an account on a user's behalf must ask `StorageAccessEvaluator` first. It is the
258
+ one place `MJ: File Storage Account Permissions` (Everyone / Role / User rows with `CanRead` / `CanWrite`) are decided,
259
+ and the one place the tracked-file rule (below) is decided:
260
+
261
+ ```typescript
262
+ import { FileStorageEngine, StorageAccessEvaluator } from '@memberjunction/storage';
263
+
264
+ const evaluator = StorageAccessEvaluator.Instance;
265
+
266
+ // Throws StorageAccountAccessDeniedError ("You do not have access to this storage account or it does not exist.")
267
+ await evaluator.AssertAccountAccess(accountId, contextUser, 'Write', provider);
268
+
269
+ // Or filter a list: returns the NormalizeUUID'd IDs the user may read
270
+ const readable = await evaluator.AccessibleAccountIDs(accountIds, contextUser, 'Read', provider);
271
+
272
+ // Object keys in an account that back an MJ: Files row the user cannot read — pass the account's driver so keys are
273
+ // compared the way that driver addresses them
274
+ const driver = await FileStorageEngine.Instance.GetDriver(accountId, contextUser);
275
+ const blocked = await evaluator.UnreadableTrackedObjectKeys(account.ProviderID, [objectKey], contextUser, provider, driver);
276
+ // Or throw TrackedFileAccessDeniedError ("You do not have access to this file or it does not exist.")
277
+ await evaluator.AssertTrackedObjectsReadable(account.ProviderID, [objectKey], contextUser, provider, driver);
278
+
279
+ // From an MJ: Files row (loaded as the caller) to its bytes: account gate + tracked-file rule, then the driver
280
+ const resolved = await FileStorageEngine.Instance.ResolveFileObject(fileRow, contextUser, 'Read', provider);
281
+ const bytes = resolved ? await resolved.Driver.GetObject({ fullPath: resolved.ObjectKey }) : null;
282
+ ```
283
+
284
+ **The account rule**
285
+
286
+ - **Evaluated per call** — nothing is cached, so a grant or revocation applies to the next call.
287
+ - **Read as the system user, decided for the caller** — the permission rows are read with the MJ system user
288
+ (`WellKnownUserSource`), so a caller who cannot read the permission table is not mistaken for an account with no rows.
289
+ - **Never truncated** — every read sets `IgnoreMaxRows`, so the entity's row cap cannot drop an account's rows (which
290
+ would make it look like an account with none) or a tracked file's row.
291
+ - **An account with no permission rows is open** to every authenticated user (the current product rule, kept in one
292
+ place in the evaluator; flipping it needs a data migration that writes explicit `Everyone` rows).
293
+ - **`Read` vs `Write`** — listing, downloading, signing a download URL and searching need `CanRead`; uploading, signing an
294
+ upload URL, copying into, moving, deleting and creating directories need `CanWrite`. A copy between accounts needs
295
+ `Read` on the source and `Write` on the destination.
296
+ - **Fails closed** — an unknown account, a malformed ID, a user without `UserRoles`, a missing system user or a failed
297
+ read all mean "no access". An unknown account is refused with the same message as a restricted one.
298
+
299
+ **The tracked-file rule**
300
+
301
+ - An object that backs an `MJ: Files` row — the row's `ProviderKey`, or `Name` when `ProviderKey` is empty, on the
302
+ account's storage provider — may be read, signed, listed or returned by search only when the caller can read that row
303
+ (entity permission and row-level security, asked as the caller). Untracked objects are governed by the account rule
304
+ alone. Rows are keyed by provider, not account, so a row in another account on the same provider counts too.
305
+ - **Writes**: an object may be overwritten (upload, upload URL, copy/move destination), moved, copied out of or deleted
306
+ only when the caller can read every row that tracks it — the minimum; the account rule still requires `CanWrite`.
307
+ `UploadFile` applies this before `PutObject`.
308
+ - **Matching** — the client's key and the row's key both go through the account driver's `NormalizeObjectKey` (default:
309
+ trim, collapse repeated `/`, strip leading/trailing `/`; S3 also strips its key prefix the way it addresses keys) and
310
+ are compared case-insensitively, so `/hr/secret.pdf`, `hr/secret.pdf/` and `HR/Secret.PDF` are the same tracked
311
+ object.
312
+ - **Refused keys** — a key with a `.`/`..` segment, a backslash, a percent-encoded `/`, `\`, `.` or control character
313
+ (`%2F`, `%5C`, `%2E`, `%00`–`%1F`, `%7F`) or a raw control character is never compared: its meaning depends on the
314
+ provider, so it is treated as an object the caller may not read. A literal `%` otherwise (`50% off.pdf`) is fine.
315
+ - **File-ID routes** (`DownloadUrl`, `GetFileContents`, `CreateMediaAccessToken`, `DeleteFile`, the artifact download
316
+ URL, the agent file handler, the realtime recording reader) go through `FileStorageEngine.ResolveFileObject`: the
317
+ row's provider resolves to its first account, which must allow the access, and the row's object gets the tracked-file
318
+ rule — so a row whose `ProviderKey`/`Name` aliases another row's object is refused. On the server, `ProviderID` and
319
+ `ProviderKey` cannot be changed on an existing row by a client save (`MJFileEntityServer`).
320
+
321
+ **Pre-authenticated URLs** are scoped to one object: Azure Blob URLs carry a blob-scoped service SAS (`sr=b`, `r` to
322
+ download, `cw` to upload), never an account SAS; every URL expires after 10 minutes.
323
+
324
+ The MJServer storage GraphQL routes, the File Storage core actions (and `List Storage Accounts`, which lists only the
325
+ accounts the caller may read), `StorageSearchProvider` and `SearchEngine`'s late permission filter all use it.
326
+
254
327
  ### Using Utility Functions
255
328
 
256
329
  The library provides high-level utility functions that work with MemberJunction's entity system:
@@ -594,6 +667,7 @@ Each provider also supports environment variable configuration. Environment vari
594
667
  | `STORAGE_GOOGLE_DRIVE_REFRESH_TOKEN` | OAuth refresh token |
595
668
  | `STORAGE_GOOGLE_DRIVE_REDIRECT_URI` | OAuth redirect URI |
596
669
  | `STORAGE_GDRIVE_ROOT_FOLDER_ID` | Root folder ID (optional) |
670
+ | `STORAGE_GDRIVE_DRIVE_ID` | Shared Drive ID. Required for a service account to upload, since a service account has no storage quota of its own. Also the default root when no root folder is set. A credential record carries it as `driveID`. |
597
671
  | `STORAGE_GDRIVE_KEY_FILE` | Service account key file (legacy) |
598
672
  | `STORAGE_GDRIVE_CREDENTIALS_JSON` | Service account credentials JSON (legacy) |
599
673