@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.
- 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/DropboxFileStorage.d.ts.map +1 -1
- package/dist/drivers/DropboxFileStorage.js +0 -51
- package/dist/drivers/DropboxFileStorage.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,4 +1,5 @@
|
|
|
1
1
|
import { Readable } from 'stream';
|
|
2
|
+
import { StorageObjectKeyNormalizer } from './ObjectKeys.js';
|
|
2
3
|
/**
|
|
3
4
|
* Represents the payload returned by the CreatePreAuthUploadUrl method.
|
|
4
5
|
* This type contains the necessary information for uploading a file to a storage provider.
|
|
@@ -369,7 +370,7 @@ export interface StorageProviderConfig {
|
|
|
369
370
|
* `driver.GetObject is not a function`. The marker turns an unresolved driver key into an
|
|
370
371
|
* immediate, named failure at the point of resolution.
|
|
371
372
|
*/
|
|
372
|
-
export declare abstract class FileStorageBase {
|
|
373
|
+
export declare abstract class FileStorageBase implements StorageObjectKeyNormalizer {
|
|
373
374
|
/**
|
|
374
375
|
* The name of this storage provider, used in error messages and logging.
|
|
375
376
|
* Each implementation must define this property with a descriptive name.
|
|
@@ -860,6 +861,21 @@ export declare abstract class FileStorageBase {
|
|
|
860
861
|
* @returns A Promise that resolves to a boolean indicating if the directory exists.
|
|
861
862
|
*/
|
|
862
863
|
abstract DirectoryExists(directoryPath: string): Promise<boolean>;
|
|
864
|
+
/**
|
|
865
|
+
* The canonical form of an object key for this storage — what the storage access checks compare when they ask whether
|
|
866
|
+
* an object backs an `MJ: Files` row (see `StorageAccessEvaluator.UnreadableTrackedObjectKeys`). Both the key a client
|
|
867
|
+
* sends and the key a row stores go through this one method, so two spellings this driver resolves to the same object
|
|
868
|
+
* must canonicalize alike.
|
|
869
|
+
*
|
|
870
|
+
* The default ({@link NormalizeStorageObjectKey}) trims the key, collapses runs of `/` and strips leading and
|
|
871
|
+
* trailing `/` — right for every driver whose spellings differ only in slashes. A driver that adds its own root or
|
|
872
|
+
* prefix to SOME keys and not others (S3's key prefix) overrides this to return the key relative to that root.
|
|
873
|
+
* Case is preserved; callers compare case-insensitively.
|
|
874
|
+
*
|
|
875
|
+
* @param objectKey - An object key as a client or an `MJ: Files` row spells it
|
|
876
|
+
* @returns The canonical key: relative to this driver's root, no leading/trailing `/`, no repeated `/`
|
|
877
|
+
*/
|
|
878
|
+
NormalizeObjectKey(objectKey: string): string;
|
|
863
879
|
/**
|
|
864
880
|
* Initialize storage provider with optional configuration.
|
|
865
881
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileStorageBase.d.ts","sourceRoot":"","sources":["../../src/generic/FileStorageBase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"FileStorageBase.d.ts","sourceRoot":"","sources":["../../src/generic/FileStorageBase.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,6 +5,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
7
|
import { RequiresSubclass } from '@memberjunction/global';
|
|
8
|
+
import { NormalizeStorageObjectKey } from './ObjectKeys.js';
|
|
8
9
|
/**
|
|
9
10
|
* Builds an HTTP `Range` header value (`bytes=Start-End` or `bytes=Start-`) from a
|
|
10
11
|
* {@link ByteRange}. Shared by streaming-capable drivers so the inclusive-range
|
|
@@ -218,6 +219,23 @@ let FileStorageBase = class FileStorageBase {
|
|
|
218
219
|
GetObjectStream(params) {
|
|
219
220
|
throw new StreamingNotSupportedError(this.providerName);
|
|
220
221
|
}
|
|
222
|
+
/**
|
|
223
|
+
* The canonical form of an object key for this storage — what the storage access checks compare when they ask whether
|
|
224
|
+
* an object backs an `MJ: Files` row (see `StorageAccessEvaluator.UnreadableTrackedObjectKeys`). Both the key a client
|
|
225
|
+
* sends and the key a row stores go through this one method, so two spellings this driver resolves to the same object
|
|
226
|
+
* must canonicalize alike.
|
|
227
|
+
*
|
|
228
|
+
* The default ({@link NormalizeStorageObjectKey}) trims the key, collapses runs of `/` and strips leading and
|
|
229
|
+
* trailing `/` — right for every driver whose spellings differ only in slashes. A driver that adds its own root or
|
|
230
|
+
* prefix to SOME keys and not others (S3's key prefix) overrides this to return the key relative to that root.
|
|
231
|
+
* Case is preserved; callers compare case-insensitively.
|
|
232
|
+
*
|
|
233
|
+
* @param objectKey - An object key as a client or an `MJ: Files` row spells it
|
|
234
|
+
* @returns The canonical key: relative to this driver's root, no leading/trailing `/`, no repeated `/`
|
|
235
|
+
*/
|
|
236
|
+
NormalizeObjectKey(objectKey) {
|
|
237
|
+
return NormalizeStorageObjectKey(objectKey);
|
|
238
|
+
}
|
|
221
239
|
/**
|
|
222
240
|
* Initialize storage provider with optional configuration.
|
|
223
241
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileStorageBase.js","sourceRoot":"","sources":["../../src/generic/FileStorageBase.ts"],"names":[],"mappings":";;;;;;AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"FileStorageBase.js","sourceRoot":"","sources":["../../src/generic/FileStorageBase.ts"],"names":[],"mappings":";;;;;;AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,yBAAyB,EAA8B,MAAM,cAAc,CAAC;AA8HrF;;;;;;;GAOG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAgB;IACnD,OAAO,KAAK,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,SAAS,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,GAAG,EAAE,CAAC;AAC3F,CAAC;AAiKD;;;;GAIG;AACH,MAAM,OAAO,yBAA0B,SAAQ,KAAK;IAClD;;;;;OAKG;IACH,YAAY,UAAkB,EAAE,YAAoB;QAClD,KAAK,CAAC,cAAc,UAAU,6BAA6B,YAAY,WAAW,CAAC,CAAC;QACpF,IAAI,CAAC,IAAI,GAAG,2BAA2B,CAAC;IAC1C,CAAC;CACF;AAED;;;;;;;;GAQG;AACH,MAAM,OAAO,0BAA2B,SAAQ,KAAK;IACnD;;;;OAIG;IACH,YAAY,YAAoB;QAC9B,KAAK,CAAC,uDAAuD,YAAY,WAAW,CAAC,CAAC;QACtF,IAAI,CAAC,IAAI,GAAG,4BAA4B,CAAC;IAC3C,CAAC;CACF;AA+CD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEI,IAAe,eAAe,GAA9B,MAAe,eAAe;IAkBnC;;;OAGG;IACH,IAAW,SAAS;QAClB,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED;;;OAGG;IACH,IAAW,WAAW;QACpB,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAED;;;;;;OAMG;IACO,8BAA8B,CAAC,UAAkB;QACzD,MAAM,IAAI,yBAAyB,CAAC,UAAU,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;IACrE,CAAC;IAED;;;;;;;;;;OAUG;IACI,OAAO;QACZ,0CAA0C;IAC5C,CAAC;IA4QD;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,IAAW,iBAAiB;QAC1B,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;;;;;;OAQG;IACH,IAAW,qBAAqB;QAC9B,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;;OAIG;IACH,IAAW,uBAAuB;QAChC,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+CG;IACI,eAAe,CAAC,MAA6B;QAClD,MAAM,IAAI,0BAA0B,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC1D,CAAC;IA4GD;;;;;;;;;;;;;OAaG;IACI,kBAAkB,CAAC,SAAiB;QACzC,OAAO,yBAAyB,CAAC,SAAS,CAAC,CAAC;IAC9C,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiEG;IACI,KAAK,CAAC,UAAU,CAAC,MAA8B;QACpD,oEAAoE;QACpE,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,SAAS,CAAC;YACnC,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC,WAAW,CAAC;QACzC,CAAC;IACH,CAAC;CA8GF,CAAA;AA3tBqB,eAAe;IADpC,gBAAgB,EAAE;GACG,eAAe,CA2tBpC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Object-key canonicalization and the key-safety rule the storage access checks use.
|
|
3
|
+
*
|
|
4
|
+
* The tracked-file rule ("an object backing an `MJ: Files` row the caller cannot read is refused") compares the key a
|
|
5
|
+
* client sends with the key an `MJ: Files` row stores. Drivers accept several spellings of the same object — S3 adds or
|
|
6
|
+
* keeps its key prefix, Box strips leading and trailing `/`, Dropbox adds a leading `/` — so a raw string comparison would
|
|
7
|
+
* let `/hr/secret.pdf` or `hr/secret.pdf/` through as "untracked" while the driver serves `hr/secret.pdf`. Both sides are
|
|
8
|
+
* therefore put through ONE canonicalizer ({@link StorageObjectKeyNormalizer}, implemented by every driver as
|
|
9
|
+
* `FileStorageBase.NormalizeObjectKey`) and compared case-insensitively.
|
|
10
|
+
*
|
|
11
|
+
* The canonical form is deliberately coarse: when two spellings might name the same object they canonicalize alike. A
|
|
12
|
+
* coarse form can only make the rule refuse more, never less.
|
|
13
|
+
*
|
|
14
|
+
* @module @memberjunction/storage
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* The object key an `MJ: Files` row resolves to: its `ProviderKey`, or its `Name` when `ProviderKey` is null or empty.
|
|
18
|
+
* The one rule every route from a row to its bytes and the tracked-file lookup use, so a row cannot be served under one
|
|
19
|
+
* key and checked under another.
|
|
20
|
+
*/
|
|
21
|
+
export declare function StorageObjectKeyOf(file: {
|
|
22
|
+
ProviderKey?: string | null;
|
|
23
|
+
Name?: string | null;
|
|
24
|
+
}): string;
|
|
25
|
+
/** Turns an object key into the canonical form the storage access checks compare. */
|
|
26
|
+
export interface StorageObjectKeyNormalizer {
|
|
27
|
+
/**
|
|
28
|
+
* The canonical form of `objectKey` for this storage: the object the driver would address, relative to the driver's
|
|
29
|
+
* own root or prefix, with no leading or trailing `/` and no repeated `/`. Case is preserved; callers compare
|
|
30
|
+
* case-insensitively.
|
|
31
|
+
*/
|
|
32
|
+
NormalizeObjectKey(objectKey: string): string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The default canonical form of an object key: trimmed, with runs of `/` collapsed to one and leading and trailing `/`
|
|
36
|
+
* removed. Case is preserved (callers compare case-insensitively).
|
|
37
|
+
*
|
|
38
|
+
* Nothing else is rewritten. A key whose meaning depends on the provider (a backslash, a `.`/`..` segment, a
|
|
39
|
+
* meaning-changing percent encoding) is refused by {@link IsSafeStorageObjectKey} before it is ever compared, so the
|
|
40
|
+
* canonical form never has to guess what a driver would make of it.
|
|
41
|
+
*/
|
|
42
|
+
export declare function NormalizeStorageObjectKey(objectKey: string): string;
|
|
43
|
+
/** The default {@link StorageObjectKeyNormalizer}: {@link NormalizeStorageObjectKey}. */
|
|
44
|
+
export declare const DEFAULT_OBJECT_KEY_NORMALIZER: StorageObjectKeyNormalizer;
|
|
45
|
+
/**
|
|
46
|
+
* Whether an object key is one the storage access checks will compare at all. A key that fails is refused (treated as
|
|
47
|
+
* an object the caller may not read) rather than canonicalized, because its meaning depends on the provider:
|
|
48
|
+
*
|
|
49
|
+
* - a `.` or `..` path segment, including one padded with whitespace or zero-width characters — a hierarchical
|
|
50
|
+
* provider (Box, Dropbox, Google Drive, SharePoint) may resolve it as traversal, so the object served need not be
|
|
51
|
+
* the one compared;
|
|
52
|
+
* - a backslash — some providers and URL layers treat it as a path separator;
|
|
53
|
+
* - a percent-encoded `/`, `\`, `.` or control character (`%2F`, `%5C`, `%2E`, `%00`–`%1F`, `%7F`) — a pre-signed URL
|
|
54
|
+
* carries the key in its path, where the provider decodes it, so `hr%2Fsecret.pdf` would be compared as one segment and
|
|
55
|
+
* served as `hr/secret.pdf`. A literal `%` followed by anything else (`50% off.pdf`, `%20`) is allowed;
|
|
56
|
+
* - a raw control character (including NUL).
|
|
57
|
+
*
|
|
58
|
+
* An empty or whitespace-only key is not unsafe — it names the root, which no `MJ: Files` row tracks.
|
|
59
|
+
*/
|
|
60
|
+
export declare function IsSafeStorageObjectKey(objectKey: string): boolean;
|
|
61
|
+
//# sourceMappingURL=ObjectKeys.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ObjectKeys.d.ts","sourceRoot":"","sources":["../../src/generic/ObjectKeys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,MAAM,CAEtG;AAED,qFAAqF;AACrF,MAAM,WAAW,0BAA0B;IACvC;;;;OAIG;IACH,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;CACjD;AAED;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CASnE;AAED,yFAAyF;AACzF,eAAO,MAAM,6BAA6B,EAAE,0BAE3C,CAAC;AAQF;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAQjE"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Object-key canonicalization and the key-safety rule the storage access checks use.
|
|
3
|
+
*
|
|
4
|
+
* The tracked-file rule ("an object backing an `MJ: Files` row the caller cannot read is refused") compares the key a
|
|
5
|
+
* client sends with the key an `MJ: Files` row stores. Drivers accept several spellings of the same object — S3 adds or
|
|
6
|
+
* keeps its key prefix, Box strips leading and trailing `/`, Dropbox adds a leading `/` — so a raw string comparison would
|
|
7
|
+
* let `/hr/secret.pdf` or `hr/secret.pdf/` through as "untracked" while the driver serves `hr/secret.pdf`. Both sides are
|
|
8
|
+
* therefore put through ONE canonicalizer ({@link StorageObjectKeyNormalizer}, implemented by every driver as
|
|
9
|
+
* `FileStorageBase.NormalizeObjectKey`) and compared case-insensitively.
|
|
10
|
+
*
|
|
11
|
+
* The canonical form is deliberately coarse: when two spellings might name the same object they canonicalize alike. A
|
|
12
|
+
* coarse form can only make the rule refuse more, never less.
|
|
13
|
+
*
|
|
14
|
+
* @module @memberjunction/storage
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* The object key an `MJ: Files` row resolves to: its `ProviderKey`, or its `Name` when `ProviderKey` is null or empty.
|
|
18
|
+
* The one rule every route from a row to its bytes and the tracked-file lookup use, so a row cannot be served under one
|
|
19
|
+
* key and checked under another.
|
|
20
|
+
*/
|
|
21
|
+
export function StorageObjectKeyOf(file) {
|
|
22
|
+
return file.ProviderKey || file.Name || '';
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The default canonical form of an object key: trimmed, with runs of `/` collapsed to one and leading and trailing `/`
|
|
26
|
+
* removed. Case is preserved (callers compare case-insensitively).
|
|
27
|
+
*
|
|
28
|
+
* Nothing else is rewritten. A key whose meaning depends on the provider (a backslash, a `.`/`..` segment, a
|
|
29
|
+
* meaning-changing percent encoding) is refused by {@link IsSafeStorageObjectKey} before it is ever compared, so the
|
|
30
|
+
* canonical form never has to guess what a driver would make of it.
|
|
31
|
+
*/
|
|
32
|
+
export function NormalizeStorageObjectKey(objectKey) {
|
|
33
|
+
// Split and join rather than regex trimming: dropping empty segments collapses runs of `/` and strips leading and
|
|
34
|
+
// trailing `/` in one linear pass with no backtracking. CodeQL flags a trailing-anchor pattern such as `/\/+$/` on
|
|
35
|
+
// caller input as polynomial (js/polynomial-redos), so this form keeps every pattern out of the path.
|
|
36
|
+
return (objectKey ?? '')
|
|
37
|
+
.trim()
|
|
38
|
+
.split('/')
|
|
39
|
+
.filter(segment => segment.length > 0)
|
|
40
|
+
.join('/');
|
|
41
|
+
}
|
|
42
|
+
/** The default {@link StorageObjectKeyNormalizer}: {@link NormalizeStorageObjectKey}. */
|
|
43
|
+
export const DEFAULT_OBJECT_KEY_NORMALIZER = {
|
|
44
|
+
NormalizeObjectKey: NormalizeStorageObjectKey
|
|
45
|
+
};
|
|
46
|
+
/** Characters whose percent-encoded form a provider or URL layer could decode into a path-changing character. */
|
|
47
|
+
const MEANING_CHANGING_ENCODED = /%(2f|5c|2e|[01][0-9a-f]|7f)/i;
|
|
48
|
+
/** ASCII control characters, including NUL. */
|
|
49
|
+
const CONTROL_CHARACTERS = /[\x00-\x1f\x7f]/;
|
|
50
|
+
/**
|
|
51
|
+
* Whether an object key is one the storage access checks will compare at all. A key that fails is refused (treated as
|
|
52
|
+
* an object the caller may not read) rather than canonicalized, because its meaning depends on the provider:
|
|
53
|
+
*
|
|
54
|
+
* - a `.` or `..` path segment, including one padded with whitespace or zero-width characters — a hierarchical
|
|
55
|
+
* provider (Box, Dropbox, Google Drive, SharePoint) may resolve it as traversal, so the object served need not be
|
|
56
|
+
* the one compared;
|
|
57
|
+
* - a backslash — some providers and URL layers treat it as a path separator;
|
|
58
|
+
* - a percent-encoded `/`, `\`, `.` or control character (`%2F`, `%5C`, `%2E`, `%00`–`%1F`, `%7F`) — a pre-signed URL
|
|
59
|
+
* carries the key in its path, where the provider decodes it, so `hr%2Fsecret.pdf` would be compared as one segment and
|
|
60
|
+
* served as `hr/secret.pdf`. A literal `%` followed by anything else (`50% off.pdf`, `%20`) is allowed;
|
|
61
|
+
* - a raw control character (including NUL).
|
|
62
|
+
*
|
|
63
|
+
* An empty or whitespace-only key is not unsafe — it names the root, which no `MJ: Files` row tracks.
|
|
64
|
+
*/
|
|
65
|
+
export function IsSafeStorageObjectKey(objectKey) {
|
|
66
|
+
if (typeof objectKey !== 'string') {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
if (objectKey.includes('\\') || CONTROL_CHARACTERS.test(objectKey) || MEANING_CHANGING_ENCODED.test(objectKey)) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
return !objectKey.split('/').map(visibleSegment).some(segment => segment === '.' || segment === '..');
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* A path segment as a filesystem would read it: Unicode format characters (`\p{Cf}`: zero-width space, joiners,
|
|
76
|
+
* word joiner, BOM, soft hyphen) removed, then trimmed. Without this, `\u200B..\u200B` would not compare equal to
|
|
77
|
+
* `..` though a driver that drops such characters would resolve it as traversal.
|
|
78
|
+
*/
|
|
79
|
+
function visibleSegment(segment) {
|
|
80
|
+
return segment.replace(/\p{Cf}/gu, '').trim();
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=ObjectKeys.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ObjectKeys.js","sourceRoot":"","sources":["../../src/generic/ObjectKeys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAA2D;IAC1F,OAAO,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC;AAC/C,CAAC;AAYD;;;;;;;GAOG;AACH,MAAM,UAAU,yBAAyB,CAAC,SAAiB;IACvD,kHAAkH;IAClH,mHAAmH;IACnH,sGAAsG;IACtG,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC;SACnB,IAAI,EAAE;SACN,KAAK,CAAC,GAAG,CAAC;SACV,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;SACrC,IAAI,CAAC,GAAG,CAAC,CAAC;AACnB,CAAC;AAED,yFAAyF;AACzF,MAAM,CAAC,MAAM,6BAA6B,GAA+B;IACrE,kBAAkB,EAAE,yBAAyB;CAChD,CAAC;AAEF,iHAAiH;AACjH,MAAM,wBAAwB,GAAG,8BAA8B,CAAC;AAEhE,+CAA+C;AAC/C,MAAM,kBAAkB,GAAG,iBAAiB,CAAC;AAE7C;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,sBAAsB,CAAC,SAAiB;IACpD,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;QAChC,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,IAAI,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,wBAAwB,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;QAC7G,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,KAAK,GAAG,IAAI,OAAO,KAAK,IAAI,CAAC,CAAC;AAC1G,CAAC;AAED;;;;GAIG;AACH,SAAS,cAAc,CAAC,OAAe;IACnC,OAAO,OAAO,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;AAClD,CAAC"}
|
package/dist/index.d.ts
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.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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
|
+
"version": "6.2.0-edge.6",
|
|
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.
|
|
25
|
-
"@memberjunction/core-entities": "6.2.0-edge.
|
|
26
|
-
"@memberjunction/credentials": "6.2.0-edge.
|
|
27
|
-
"@memberjunction/global": "6.2.0-edge.
|
|
28
|
-
"@memberjunction/network-utils": "6.2.0-edge.
|
|
24
|
+
"@memberjunction/core": "6.2.0-edge.6",
|
|
25
|
+
"@memberjunction/core-entities": "6.2.0-edge.6",
|
|
26
|
+
"@memberjunction/credentials": "6.2.0-edge.6",
|
|
27
|
+
"@memberjunction/global": "6.2.0-edge.6",
|
|
28
|
+
"@memberjunction/network-utils": "6.2.0-edge.6",
|
|
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
|
|