@hanzo/s3 0.6.3 → 8.0.7

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 (119) hide show
  1. package/LICENSE +202 -0
  2. package/MAINTAINERS.md +62 -0
  3. package/README.md +262 -0
  4. package/README_zh_CN.md +192 -0
  5. package/dist/esm/AssumeRoleProvider.d.mts +86 -0
  6. package/dist/esm/AssumeRoleProvider.mjs +183 -0
  7. package/dist/esm/CredentialProvider.d.mts +22 -0
  8. package/dist/esm/CredentialProvider.mjs +48 -0
  9. package/dist/esm/Credentials.d.mts +22 -0
  10. package/dist/esm/Credentials.mjs +38 -0
  11. package/dist/esm/IamAwsProvider.d.mts +27 -0
  12. package/dist/esm/IamAwsProvider.mjs +189 -0
  13. package/dist/esm/errors.d.mts +82 -0
  14. package/dist/esm/errors.mjs +117 -0
  15. package/dist/esm/helpers.d.mts +156 -0
  16. package/dist/esm/helpers.mjs +218 -0
  17. package/dist/esm/internal/async.d.mts +9 -0
  18. package/dist/esm/internal/async.mjs +14 -0
  19. package/dist/esm/internal/callbackify.d.mts +1 -0
  20. package/dist/esm/internal/callbackify.mjs +15 -0
  21. package/dist/esm/internal/client.d.mts +394 -0
  22. package/dist/esm/internal/client.mjs +3007 -0
  23. package/dist/esm/internal/copy-conditions.d.mts +10 -0
  24. package/dist/esm/internal/copy-conditions.mjs +25 -0
  25. package/dist/esm/internal/extensions.d.mts +18 -0
  26. package/dist/esm/internal/extensions.mjs +114 -0
  27. package/dist/esm/internal/helper.d.mts +177 -0
  28. package/dist/esm/internal/helper.mjs +552 -0
  29. package/dist/esm/internal/join-host-port.d.mts +11 -0
  30. package/dist/esm/internal/join-host-port.mjs +23 -0
  31. package/dist/esm/internal/post-policy.d.mts +17 -0
  32. package/dist/esm/internal/post-policy.mjs +98 -0
  33. package/dist/esm/internal/request.d.mts +11 -0
  34. package/dist/esm/internal/request.mjs +75 -0
  35. package/dist/esm/internal/response.d.mts +8 -0
  36. package/dist/esm/internal/response.mjs +16 -0
  37. package/dist/esm/internal/s3-endpoints.d.mts +38 -0
  38. package/dist/esm/internal/s3-endpoints.mjs +68 -0
  39. package/dist/esm/internal/type.d.mts +482 -0
  40. package/dist/esm/internal/type.mjs +30 -0
  41. package/dist/esm/internal/xml-parser.d.mts +93 -0
  42. package/dist/esm/internal/xml-parser.mjs +819 -0
  43. package/dist/esm/notification.d.mts +58 -0
  44. package/dist/esm/notification.mjs +209 -0
  45. package/dist/esm/s3.d.mts +40 -0
  46. package/dist/esm/s3.mjs +86 -0
  47. package/dist/esm/signing.d.mts +5 -0
  48. package/dist/esm/signing.mjs +258 -0
  49. package/dist/main/AssumeRoleProvider.d.ts +86 -0
  50. package/dist/main/AssumeRoleProvider.js +191 -0
  51. package/dist/main/CredentialProvider.d.ts +22 -0
  52. package/dist/main/CredentialProvider.js +55 -0
  53. package/dist/main/Credentials.d.ts +22 -0
  54. package/dist/main/Credentials.js +45 -0
  55. package/dist/main/IamAwsProvider.d.ts +27 -0
  56. package/dist/main/IamAwsProvider.js +198 -0
  57. package/dist/main/errors.d.ts +82 -0
  58. package/dist/main/errors.js +138 -0
  59. package/dist/main/helpers.d.ts +156 -0
  60. package/dist/main/helpers.js +233 -0
  61. package/dist/main/internal/async.d.ts +9 -0
  62. package/dist/main/internal/async.js +24 -0
  63. package/dist/main/internal/callbackify.d.ts +1 -0
  64. package/dist/main/internal/callbackify.js +21 -0
  65. package/dist/main/internal/client.d.ts +394 -0
  66. package/dist/main/internal/client.js +3014 -0
  67. package/dist/main/internal/copy-conditions.d.ts +10 -0
  68. package/dist/main/internal/copy-conditions.js +31 -0
  69. package/dist/main/internal/extensions.d.ts +18 -0
  70. package/dist/main/internal/extensions.js +122 -0
  71. package/dist/main/internal/helper.d.ts +177 -0
  72. package/dist/main/internal/helper.js +608 -0
  73. package/dist/main/internal/join-host-port.d.ts +11 -0
  74. package/dist/main/internal/join-host-port.js +29 -0
  75. package/dist/main/internal/post-policy.d.ts +17 -0
  76. package/dist/main/internal/post-policy.js +107 -0
  77. package/dist/main/internal/request.d.ts +11 -0
  78. package/dist/main/internal/request.js +83 -0
  79. package/dist/main/internal/response.d.ts +8 -0
  80. package/dist/main/internal/response.js +24 -0
  81. package/dist/main/internal/s3-endpoints.d.ts +38 -0
  82. package/dist/main/internal/s3-endpoints.js +73 -0
  83. package/dist/main/internal/type.d.ts +482 -0
  84. package/dist/main/internal/type.js +42 -0
  85. package/dist/main/internal/xml-parser.d.ts +93 -0
  86. package/dist/main/internal/xml-parser.js +849 -0
  87. package/dist/main/notification.d.ts +58 -0
  88. package/dist/main/notification.js +230 -0
  89. package/dist/main/s3.d.ts +40 -0
  90. package/dist/main/s3.js +117 -0
  91. package/dist/main/signing.d.ts +5 -0
  92. package/dist/main/signing.js +269 -0
  93. package/package.json +146 -39
  94. package/src/AssumeRoleProvider.ts +262 -0
  95. package/src/CredentialProvider.ts +54 -0
  96. package/src/Credentials.ts +44 -0
  97. package/src/IamAwsProvider.ts +234 -0
  98. package/src/errors.ts +120 -0
  99. package/src/helpers.ts +354 -0
  100. package/src/internal/async.ts +14 -0
  101. package/src/internal/callbackify.ts +19 -0
  102. package/src/internal/client.ts +3412 -0
  103. package/src/internal/copy-conditions.ts +30 -0
  104. package/src/internal/extensions.ts +140 -0
  105. package/src/internal/helper.ts +606 -0
  106. package/src/internal/join-host-port.ts +23 -0
  107. package/src/internal/post-policy.ts +99 -0
  108. package/src/internal/request.ts +102 -0
  109. package/src/internal/response.ts +26 -0
  110. package/src/internal/s3-endpoints.ts +70 -0
  111. package/src/internal/type.ts +577 -0
  112. package/src/internal/xml-parser.ts +871 -0
  113. package/src/notification.ts +254 -0
  114. package/src/s3.ts +155 -0
  115. package/src/signing.ts +325 -0
  116. package/lib/index.js +0 -450
  117. package/lib/index.js.map +0 -7
  118. package/lib/perfTest.js +0 -91
  119. package/lib/perfTest.js.map +0 -7
package/src/signing.ts ADDED
@@ -0,0 +1,325 @@
1
+ /*
2
+ * Hanzo S3 Javascript Library for Amazon S3 Compatible Cloud Storage, (C) 2016 Hanzo AI, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import * as crypto from 'node:crypto'
18
+
19
+ import * as errors from './errors.ts'
20
+ import { PRESIGN_EXPIRY_DAYS_MAX } from './helpers.ts'
21
+ import { getScope, isNumber, isObject, isString, makeDateLong, makeDateShort, uriEscape } from './internal/helper.ts'
22
+ import type { ICanonicalRequest, IRequest, RequestHeaders } from './internal/type.ts'
23
+
24
+ const signV4Algorithm = 'AWS4-HMAC-SHA256'
25
+
26
+ // getCanonicalRequest generate a canonical request of style.
27
+ //
28
+ // canonicalRequest =
29
+ // <HTTPMethod>\n
30
+ // <CanonicalURI>\n
31
+ // <CanonicalQueryString>\n
32
+ // <CanonicalHeaders>\n
33
+ // <SignedHeaders>\n
34
+ // <HashedPayload>
35
+ //
36
+ function getCanonicalRequest(
37
+ method: string,
38
+ path: string,
39
+ headers: RequestHeaders,
40
+ signedHeaders: string[],
41
+ hashedPayload: string,
42
+ ): ICanonicalRequest {
43
+ if (!isString(method)) {
44
+ throw new TypeError('method should be of type "string"')
45
+ }
46
+ if (!isString(path)) {
47
+ throw new TypeError('path should be of type "string"')
48
+ }
49
+ if (!isObject(headers)) {
50
+ throw new TypeError('headers should be of type "object"')
51
+ }
52
+ if (!Array.isArray(signedHeaders)) {
53
+ throw new TypeError('signedHeaders should be of type "array"')
54
+ }
55
+ if (!isString(hashedPayload)) {
56
+ throw new TypeError('hashedPayload should be of type "string"')
57
+ }
58
+
59
+ const headersArray = signedHeaders.reduce((acc, i) => {
60
+ // Trim spaces from the value (required by V4 spec)
61
+ const val = `${headers[i]}`.replace(/ +/g, ' ')
62
+ acc.push(`${i.toLowerCase()}:${val}`)
63
+ return acc
64
+ }, [] as string[])
65
+
66
+ const requestResource = path.split('?')[0]
67
+ let requestQuery = path.split('?')[1]
68
+ if (!requestQuery) {
69
+ requestQuery = ''
70
+ }
71
+
72
+ if (requestQuery) {
73
+ requestQuery = requestQuery
74
+ .split('&')
75
+ .sort()
76
+ .map((element) => (!element.includes('=') ? element + '=' : element))
77
+ .join('&')
78
+ }
79
+
80
+ return [
81
+ method.toUpperCase(),
82
+ requestResource,
83
+ requestQuery,
84
+ headersArray.join('\n') + '\n',
85
+ signedHeaders.join(';').toLowerCase(),
86
+ hashedPayload,
87
+ ].join('\n')
88
+ }
89
+
90
+ // generate a credential string
91
+ function getCredential(accessKey: string, region: string, requestDate?: Date, serviceName = 's3') {
92
+ if (!isString(accessKey)) {
93
+ throw new TypeError('accessKey should be of type "string"')
94
+ }
95
+ if (!isString(region)) {
96
+ throw new TypeError('region should be of type "string"')
97
+ }
98
+ if (!isObject(requestDate)) {
99
+ throw new TypeError('requestDate should be of type "object"')
100
+ }
101
+ return `${accessKey}/${getScope(region, requestDate, serviceName)}`
102
+ }
103
+
104
+ // Returns signed headers array - alphabetically sorted
105
+ function getSignedHeaders(headers: RequestHeaders): string[] {
106
+ if (!isObject(headers)) {
107
+ throw new TypeError('request should be of type "object"')
108
+ }
109
+ // Excerpts from @lsegal - https://github.com/aws/aws-sdk-js/issues/659#issuecomment-120477258
110
+ //
111
+ // User-Agent:
112
+ //
113
+ // This is ignored from signing because signing this causes problems with generating pre-signed URLs
114
+ // (that are executed by other agents) or when customers pass requests through proxies, which may
115
+ // modify the user-agent.
116
+ //
117
+ // Content-Length:
118
+ //
119
+ // This is ignored from signing because generating a pre-signed URL should not provide a content-length
120
+ // constraint, specifically when vending a S3 pre-signed PUT URL. The corollary to this is that when
121
+ // sending regular requests (non-pre-signed), the signature contains a checksum of the body, which
122
+ // implicitly validates the payload length (since changing the number of bytes would change the checksum)
123
+ // and therefore this header is not valuable in the signature.
124
+ //
125
+ // Content-Type:
126
+ //
127
+ // Signing this header causes quite a number of problems in browser environments, where browsers
128
+ // like to modify and normalize the content-type header in different ways. There is more information
129
+ // on this in https://github.com/aws/aws-sdk-js/issues/244. Avoiding this field simplifies logic
130
+ // and reduces the possibility of future bugs
131
+ //
132
+ // Authorization:
133
+ //
134
+ // Is skipped for obvious reasons
135
+
136
+ const ignoredHeaders = ['authorization', 'content-length', 'content-type', 'user-agent']
137
+ return Object.keys(headers)
138
+ .filter((header) => !ignoredHeaders.includes(header))
139
+ .sort()
140
+ }
141
+
142
+ // returns the key used for calculating signature
143
+ function getSigningKey(date: Date, region: string, secretKey: string, serviceName = 's3') {
144
+ if (!isObject(date)) {
145
+ throw new TypeError('date should be of type "object"')
146
+ }
147
+ if (!isString(region)) {
148
+ throw new TypeError('region should be of type "string"')
149
+ }
150
+ if (!isString(secretKey)) {
151
+ throw new TypeError('secretKey should be of type "string"')
152
+ }
153
+ const dateLine = makeDateShort(date)
154
+ const hmac1 = crypto
155
+ .createHmac('sha256', 'AWS4' + secretKey)
156
+ .update(dateLine)
157
+ .digest(),
158
+ hmac2 = crypto.createHmac('sha256', hmac1).update(region).digest(),
159
+ hmac3 = crypto.createHmac('sha256', hmac2).update(serviceName).digest()
160
+ return crypto.createHmac('sha256', hmac3).update('aws4_request').digest()
161
+ }
162
+
163
+ // returns the string that needs to be signed
164
+ function getStringToSign(canonicalRequest: ICanonicalRequest, requestDate: Date, region: string, serviceName = 's3') {
165
+ if (!isString(canonicalRequest)) {
166
+ throw new TypeError('canonicalRequest should be of type "string"')
167
+ }
168
+ if (!isObject(requestDate)) {
169
+ throw new TypeError('requestDate should be of type "object"')
170
+ }
171
+ if (!isString(region)) {
172
+ throw new TypeError('region should be of type "string"')
173
+ }
174
+ const hash = crypto.createHash('sha256').update(canonicalRequest).digest('hex')
175
+ const scope = getScope(region, requestDate, serviceName)
176
+ const stringToSign = [signV4Algorithm, makeDateLong(requestDate), scope, hash]
177
+
178
+ return stringToSign.join('\n')
179
+ }
180
+
181
+ // calculate the signature of the POST policy
182
+ export function postPresignSignatureV4(region: string, date: Date, secretKey: string, policyBase64: string): string {
183
+ if (!isString(region)) {
184
+ throw new TypeError('region should be of type "string"')
185
+ }
186
+ if (!isObject(date)) {
187
+ throw new TypeError('date should be of type "object"')
188
+ }
189
+ if (!isString(secretKey)) {
190
+ throw new TypeError('secretKey should be of type "string"')
191
+ }
192
+ if (!isString(policyBase64)) {
193
+ throw new TypeError('policyBase64 should be of type "string"')
194
+ }
195
+ const signingKey = getSigningKey(date, region, secretKey)
196
+ return crypto.createHmac('sha256', signingKey).update(policyBase64).digest('hex').toLowerCase()
197
+ }
198
+
199
+ // Returns the authorization header
200
+ export function signV4(
201
+ request: IRequest,
202
+ accessKey: string,
203
+ secretKey: string,
204
+ region: string,
205
+ requestDate: Date,
206
+ sha256sum: string,
207
+ serviceName = 's3',
208
+ ) {
209
+ if (!isObject(request)) {
210
+ throw new TypeError('request should be of type "object"')
211
+ }
212
+ if (!isString(accessKey)) {
213
+ throw new TypeError('accessKey should be of type "string"')
214
+ }
215
+ if (!isString(secretKey)) {
216
+ throw new TypeError('secretKey should be of type "string"')
217
+ }
218
+ if (!isString(region)) {
219
+ throw new TypeError('region should be of type "string"')
220
+ }
221
+
222
+ if (!accessKey) {
223
+ throw new errors.AccessKeyRequiredError('accessKey is required for signing')
224
+ }
225
+ if (!secretKey) {
226
+ throw new errors.SecretKeyRequiredError('secretKey is required for signing')
227
+ }
228
+
229
+ const signedHeaders = getSignedHeaders(request.headers)
230
+ const canonicalRequest = getCanonicalRequest(request.method, request.path, request.headers, signedHeaders, sha256sum)
231
+ const serviceIdentifier = serviceName || 's3'
232
+ const stringToSign = getStringToSign(canonicalRequest, requestDate, region, serviceIdentifier)
233
+ const signingKey = getSigningKey(requestDate, region, secretKey, serviceIdentifier)
234
+ const credential = getCredential(accessKey, region, requestDate, serviceIdentifier)
235
+ const signature = crypto.createHmac('sha256', signingKey).update(stringToSign).digest('hex').toLowerCase()
236
+
237
+ return `${signV4Algorithm} Credential=${credential}, SignedHeaders=${signedHeaders
238
+ .join(';')
239
+ .toLowerCase()}, Signature=${signature}`
240
+ }
241
+
242
+ export function signV4ByServiceName(
243
+ request: IRequest,
244
+ accessKey: string,
245
+ secretKey: string,
246
+ region: string,
247
+ requestDate: Date,
248
+ contentSha256: string,
249
+ serviceName = 's3',
250
+ ): string {
251
+ return signV4(request, accessKey, secretKey, region, requestDate, contentSha256, serviceName)
252
+ }
253
+
254
+ // returns a presigned URL string
255
+ export function presignSignatureV4(
256
+ request: IRequest,
257
+ accessKey: string,
258
+ secretKey: string,
259
+ sessionToken: string | undefined,
260
+ region: string,
261
+ requestDate: Date,
262
+ expires: number | undefined,
263
+ ) {
264
+ if (!isObject(request)) {
265
+ throw new TypeError('request should be of type "object"')
266
+ }
267
+ if (!isString(accessKey)) {
268
+ throw new TypeError('accessKey should be of type "string"')
269
+ }
270
+ if (!isString(secretKey)) {
271
+ throw new TypeError('secretKey should be of type "string"')
272
+ }
273
+ if (!isString(region)) {
274
+ throw new TypeError('region should be of type "string"')
275
+ }
276
+
277
+ if (!accessKey) {
278
+ throw new errors.AccessKeyRequiredError('accessKey is required for presigning')
279
+ }
280
+ if (!secretKey) {
281
+ throw new errors.SecretKeyRequiredError('secretKey is required for presigning')
282
+ }
283
+
284
+ if (expires && !isNumber(expires)) {
285
+ throw new TypeError('expires should be of type "number"')
286
+ }
287
+ if (expires && expires < 1) {
288
+ throw new errors.ExpiresParamError('expires param cannot be less than 1 seconds')
289
+ }
290
+ if (expires && expires > PRESIGN_EXPIRY_DAYS_MAX) {
291
+ throw new errors.ExpiresParamError('expires param cannot be greater than 7 days')
292
+ }
293
+
294
+ const iso8601Date = makeDateLong(requestDate)
295
+ const signedHeaders = getSignedHeaders(request.headers)
296
+ const credential = getCredential(accessKey, region, requestDate)
297
+ const hashedPayload = 'UNSIGNED-PAYLOAD'
298
+
299
+ const requestQuery: string[] = []
300
+ requestQuery.push(`X-Amz-Algorithm=${signV4Algorithm}`)
301
+ requestQuery.push(`X-Amz-Credential=${uriEscape(credential)}`)
302
+ requestQuery.push(`X-Amz-Date=${iso8601Date}`)
303
+ requestQuery.push(`X-Amz-Expires=${expires}`)
304
+ requestQuery.push(`X-Amz-SignedHeaders=${uriEscape(signedHeaders.join(';').toLowerCase())}`)
305
+ if (sessionToken) {
306
+ requestQuery.push(`X-Amz-Security-Token=${uriEscape(sessionToken)}`)
307
+ }
308
+
309
+ const resource = request.path.split('?')[0]
310
+ let query = request.path.split('?')[1]
311
+ if (query) {
312
+ query = query + '&' + requestQuery.join('&')
313
+ } else {
314
+ query = requestQuery.join('&')
315
+ }
316
+
317
+ const path = resource + '?' + query
318
+
319
+ const canonicalRequest = getCanonicalRequest(request.method, path, request.headers, signedHeaders, hashedPayload)
320
+
321
+ const stringToSign = getStringToSign(canonicalRequest, requestDate, region)
322
+ const signingKey = getSigningKey(requestDate, region, secretKey)
323
+ const signature = crypto.createHmac('sha256', signingKey).update(stringToSign).digest('hex').toLowerCase()
324
+ return request.protocol + '//' + request.headers.host + path + `&X-Amz-Signature=${signature}`
325
+ }