@remit/storage-service 0.0.12 → 0.0.14
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/backends/filesystem.d.ts +12 -0
- package/dist/backends/filesystem.js +296 -0
- package/dist/backends/s3.d.ts +3 -0
- package/dist/backends/s3.js +370 -0
- package/dist/filesystem.js +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/outbox-sweep.d.ts +33 -0
- package/dist/outbox-sweep.js +37 -0
- package/dist/outbox-upload-url.d.ts +44 -0
- package/{src/outbox-upload-url.ts → dist/outbox-upload-url.js} +20 -50
- package/dist/s3.d.ts +3 -0
- package/dist/s3.js +28 -0
- package/dist/signed-path.d.ts +14 -0
- package/dist/signed-path.js +46 -0
- package/dist/storage.d.ts +241 -0
- package/dist/storage.js +219 -0
- package/dist/uri.d.ts +9 -0
- package/dist/uri.js +28 -0
- package/package.json +51 -46
- package/src/backends/filesystem.ts +0 -517
- package/src/backends/s3.test.ts +0 -447
- package/src/backends/s3.ts +0 -624
- package/src/index.ts +0 -53
- package/src/outbox-attachment-retrieve.test.ts +0 -56
- package/src/outbox-sweep.test.ts +0 -196
- package/src/outbox-sweep.ts +0 -103
- package/src/outbox-upload-url.test.ts +0 -290
- package/src/s3.ts +0 -36
- package/src/signed-path.ts +0 -86
- package/src/storage.test.ts +0 -646
- package/src/storage.ts +0 -699
- package/src/uri.ts +0 -47
- package/tsconfig.json +0 -8
- /package/{src/filesystem.ts → dist/filesystem.d.ts} +0 -0
package/src/signed-path.ts
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* The HMAC scheme behind every signed storage URL this stack mints.
|
|
5
|
-
*
|
|
6
|
-
* It started as the read side — `/content/*` is served against a signature
|
|
7
|
-
* because a bearer token cannot ride on an `<img src>` inside email HTML — and
|
|
8
|
-
* the write side needs exactly the same capability: a URL that grants access to
|
|
9
|
-
* one path, for a while, and is unforgeable without the key. Rather than a
|
|
10
|
-
* second scheme, both use this one with a different derivation label, so a
|
|
11
|
-
* signature minted to read content can never be replayed as permission to
|
|
12
|
-
* write, and vice versa.
|
|
13
|
-
*
|
|
14
|
-
* The signed message is the storage path followed by every claim in a fixed
|
|
15
|
-
* order. Because the path is part of the message, a signature minted for one
|
|
16
|
-
* account's object cannot be presented for another's.
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
const deriveSigningKey = (masterSecret: string, label: string): Buffer =>
|
|
20
|
-
createHmac("sha256", masterSecret).update(label).digest();
|
|
21
|
-
|
|
22
|
-
const canonicalMessage = (
|
|
23
|
-
relativePath: string,
|
|
24
|
-
claims: readonly number[],
|
|
25
|
-
): string => [relativePath, ...claims].join("\n");
|
|
26
|
-
|
|
27
|
-
const computeSignature = (
|
|
28
|
-
key: Buffer,
|
|
29
|
-
relativePath: string,
|
|
30
|
-
claims: readonly number[],
|
|
31
|
-
): string =>
|
|
32
|
-
createHmac("sha256", key)
|
|
33
|
-
.update(canonicalMessage(relativePath, claims))
|
|
34
|
-
.digest("base64url");
|
|
35
|
-
|
|
36
|
-
export const signStoragePath = (
|
|
37
|
-
masterSecret: string,
|
|
38
|
-
label: string,
|
|
39
|
-
relativePath: string,
|
|
40
|
-
claims: readonly number[],
|
|
41
|
-
): string =>
|
|
42
|
-
computeSignature(deriveSigningKey(masterSecret, label), relativePath, claims);
|
|
43
|
-
|
|
44
|
-
export type SignedPathFailure =
|
|
45
|
-
| "missing"
|
|
46
|
-
| "malformed"
|
|
47
|
-
| "expired"
|
|
48
|
-
| "bad-signature";
|
|
49
|
-
|
|
50
|
-
export type SignedPathResult =
|
|
51
|
-
| { valid: true }
|
|
52
|
-
| { valid: false; reason: SignedPathFailure };
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Verify a presented signature against the path and claims it must cover. The
|
|
56
|
-
* first claim is always the expiry, in unix seconds. Pure, so the decision can
|
|
57
|
-
* be unit-tested without a live server.
|
|
58
|
-
*/
|
|
59
|
-
export const verifyStoragePath = (
|
|
60
|
-
masterSecret: string,
|
|
61
|
-
label: string,
|
|
62
|
-
relativePath: string,
|
|
63
|
-
claims: readonly number[],
|
|
64
|
-
sig: string | undefined,
|
|
65
|
-
nowSeconds: number,
|
|
66
|
-
): SignedPathResult => {
|
|
67
|
-
if (!sig || claims.length === 0) return { valid: false, reason: "missing" };
|
|
68
|
-
|
|
69
|
-
const [exp] = claims;
|
|
70
|
-
if (!claims.every((claim) => Number.isInteger(claim) && claim > 0)) {
|
|
71
|
-
return { valid: false, reason: "malformed" };
|
|
72
|
-
}
|
|
73
|
-
if (exp < nowSeconds) return { valid: false, reason: "expired" };
|
|
74
|
-
|
|
75
|
-
const expected = Buffer.from(
|
|
76
|
-
signStoragePath(masterSecret, label, relativePath, claims),
|
|
77
|
-
);
|
|
78
|
-
const presented = Buffer.from(sig);
|
|
79
|
-
if (expected.length !== presented.length) {
|
|
80
|
-
return { valid: false, reason: "bad-signature" };
|
|
81
|
-
}
|
|
82
|
-
if (!timingSafeEqual(expected, presented)) {
|
|
83
|
-
return { valid: false, reason: "bad-signature" };
|
|
84
|
-
}
|
|
85
|
-
return { valid: true };
|
|
86
|
-
};
|