Skip to content

Commit eab7890

Browse files
committed
fix: correct errors revealed by testing a deployment
1 parent 6863bad commit eab7890

File tree

3 files changed

+42
-26
lines changed

3 files changed

+42
-26
lines changed

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1172,7 +1172,9 @@ When asset proxying is enabled, two endpoints are available for accessing proxie
11721172

11731173
### IAM Permissions
11741174

1175-
For the Asset Proxy feature to generate pre-signed URLs, the API and ingest Lambdas must be assigned permissions for the S3 buckets containing the assets. Add the following to the IAM role statements in your `serverless.yml` file, adjusting the resources as needed:
1175+
For the Asset Proxy feature to generate pre-signed URLs, the API and ingest Lambdas must
1176+
be assigned permissions for the S3 buckets containing the assets. Add the following to the
1177+
IAM role statements in your `serverless.yml` file, adjusting the resources as needed:
11761178

11771179
For the `LIST` mode, you can specify the buckets listed in `ASSET_PROXY_BUCKET_LIST`:
11781180

@@ -1186,6 +1188,7 @@ For the `LIST` mode, you can specify the buckets listed in `ASSET_PROXY_BUCKET_L
11861188
- Effect: Allow
11871189
Action:
11881190
- s3:HeadBucket
1191+
- s3:ListBucket
11891192
Resource:
11901193
- "arn:aws:s3:::my-bucket-1"
11911194
- "arn:aws:s3:::my-bucket-2"
@@ -1201,10 +1204,12 @@ For the `ALL` mode, use wildcards:
12011204
- Effect: Allow
12021205
Action:
12031206
- s3:HeadBucket
1207+
- s3:ListBucket
12041208
Resource: "arn:aws:s3:::*"
12051209
```
12061210

1207-
When using `ALL_BUCKETS_IN_ACCOUNT` mode, the Lambda also needs permission to list buckets:
1211+
When using `ALL_BUCKETS_IN_ACCOUNT` mode, the Lambda also needs permission to list the
1212+
account buckets:
12081213

12091214
```yaml
12101215
- Effect: Allow
@@ -1214,6 +1219,7 @@ When using `ALL_BUCKETS_IN_ACCOUNT` mode, the Lambda also needs permission to li
12141219
- Effect: Allow
12151220
Action:
12161221
- s3:HeadBucket
1222+
- s3:ListBucket
12171223
Resource: "arn:aws:s3:::*"
12181224
- Effect: Allow
12191225
Action:

src/lib/asset-buckets.js

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import {
55
import { s3 } from './aws-clients.js'
66
import logger from './logger.js'
77

8-
const s3Client = s3()
8+
// Follow, rather than throw, HeadBucket redirects for buckets not in the client's region
9+
const s3Client = s3({ followRegionRedirects: true })
910

1011
export const BucketOptionEnum = Object.freeze({
1112
NONE: 'NONE',
@@ -56,9 +57,10 @@ export class AssetBuckets {
5657
)
5758
}
5859

59-
const count = Object.keys(this.bucketCache).length
60+
const bucketNames = Object.keys(this.bucketCache)
6061
logger.info(
61-
`Parsed ${count} buckets from ASSET_PROXY_BUCKET_LIST for asset proxy`
62+
`Parsed ${bucketNames.length} buckets from ASSET_PROXY_BUCKET_LIST `
63+
+ `for asset proxy: ${bucketNames.join(', ')}`
6264
)
6365
} else {
6466
throw new Error(
@@ -80,9 +82,10 @@ export class AssetBuckets {
8082
.map(async (name) => { await this.getBucket(name) })
8183
)
8284

83-
const count = Object.keys(this.bucketCache).length
85+
const bucketNames = Object.keys(this.bucketCache)
8486
logger.info(
85-
`Fetched ${count} buckets from AWS account for asset proxy`
87+
`Fetched ${bucketNames.length} buckets from AWS account `
88+
+ `for asset proxy: ${bucketNames.join(', ')}`
8689
)
8790
break
8891
}
@@ -99,29 +102,33 @@ export class AssetBuckets {
99102
async getBucket(bucketName) {
100103
if (!(bucketName in this.bucketCache)) {
101104
const command = new HeadBucketCommand({ Bucket: bucketName })
102-
const response = await s3Client.send(command)
103-
const statusCode = response.$metadata.httpStatusCode
104105
let name = null
105106
let region = null
106107

107-
switch (statusCode) {
108-
case 200:
108+
try {
109+
const response = await s3Client.send(command)
109110
name = bucketName
110111
region = response.BucketRegion === 'EU'
111112
? 'eu-west-1'
112113
: response.BucketRegion || 'us-east-1'
113-
break
114-
case 403:
115-
logger.warn(`Access denied to bucket ${bucketName}`)
116-
break
117-
case 404:
118-
logger.warn(`Bucket ${bucketName} does not exist`)
119-
break
120-
case 400:
121-
logger.warn(`Bad request for bucket ${bucketName}`)
122-
break
123-
default:
124-
logger.warn(`Unexpected status code ${statusCode} for bucket ${bucketName}`)
114+
} catch (err) {
115+
const error = /** @type {any} */ (err)
116+
const statusCode = error.$metadata?.httpStatusCode
117+
118+
switch (statusCode) {
119+
case 403:
120+
logger.warn(`Access denied to bucket ${bucketName}`)
121+
break
122+
case 404:
123+
logger.warn(`Bucket ${bucketName} does not exist`)
124+
break
125+
case 400:
126+
logger.warn(`Bad request for bucket ${bucketName}`)
127+
break
128+
default:
129+
logger.error(`Unexpected error for bucket ${bucketName}:`, error)
130+
throw error
131+
}
125132
}
126133

127134
this.bucketCache[bucketName] = { name, region }

tests/unit/test-asset-buckets.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ test('AssetBuckets - LIST mode throws if bucket list is null', async (t) => {
5050
})
5151

5252
test('AssetBuckets - LIST mode throws if bucket is inaccessible', async (t) => {
53-
s3Mock.on(HeadBucketCommand).resolves({
53+
s3Mock.on(HeadBucketCommand).rejects({
54+
name: '403',
5455
$metadata: { httpStatusCode: 403 }
5556
})
5657

@@ -130,7 +131,8 @@ test('AssetBuckets - shouldProxyBucket with ALL_BUCKETS_IN_ACCOUNT mode only pro
130131

131132
// Using serial to prevent HeadBucketCommand mock interference between tests
132133
test.serial('AssetBuckets - getBucket handles 403 access denied', async (t) => {
133-
s3Mock.on(HeadBucketCommand).resolves({
134+
s3Mock.on(HeadBucketCommand).rejects({
135+
name: '403',
134136
$metadata: { httpStatusCode: 403 }
135137
})
136138

@@ -143,7 +145,8 @@ test.serial('AssetBuckets - getBucket handles 403 access denied', async (t) => {
143145

144146
// Using serial to prevent HeadBucketCommand mock interference between tests
145147
test.serial('AssetBuckets - getBucket handles 404 not found', async (t) => {
146-
s3Mock.on(HeadBucketCommand).resolves({
148+
s3Mock.on(HeadBucketCommand).rejects({
149+
name: '404',
147150
$metadata: { httpStatusCode: 404 }
148151
})
149152

0 commit comments

Comments
 (0)