Open mayteio opened 4 years ago
Hi @mayteio - can you give me more details on the scenario you are looking to enable with this? How is your bucket structured?
Sure thing. Backstory, we use Esri products to manage a lot of geospatial data. The data management tools they have leave a lot to be desired, so we've built our own CMS on top of it that uses Esri for core things like storing geospatial data, basic field management, etc.
We use AWS to manage file uploads per geospatial feature, as this is functionality esri doesn't have. We are using S3 via amplify to manage these uploads (we have more plans to use amplify in this project), storing the object key in an esri field.
We want these files to be publicly available, so when someone is looking at the feature, they can pull the image from S3 without logging in, and not necessarily from an app that is configured for this particular amplify project (so unauth access is out of the question). Thus the need for ACL.
Thanks a lot for sharing this @mayteio, much appreciated the suggestion and resolution. It is a no brainer to me either and I expect it to work out of box. (One thing I would add is to be mindful of public access s3PermissionsAuthenticatedPublic
, which gives every user the permission to edit the file. I would give PutObjectAcl
to protected access s3PermissionsAuthenticatedProtected
to limit the editing permission to the owner only.)
@renebrandel To give another scenario. The github page we are currently on where we get user profile images is a perfect example.
The profile image source is structured with this format https://avatars1.githubusercontent.com/u/{userId}
(the prefix can be thought of as a bucket). Once the page is loaded with the user's id, it requests the image source directly at https://{bucket-prefix}/{userId}
. The data should be publicly available.
Would be create if we could include this as part of the permission configuration of the function or in general
function-parameters.json
{
"permissions": {
"storage": {
"s3images": [
"create",
"read",
"update",
"delete",
"updateacl"
]
}
},
"lambdaLayers": []
}
For now I just added it manually to the policy and seems to persist.
For what it is worth, I also like mayteio's idea of manually adding the permission to the parameters is easy, however, might trip up inexperienced developers who (rightly) expect the aws-amplify/Storage.put option 'acl: public-read' to work out of the box. It is what I intuitively tried to do before reading documentation.
As of amplify 7.6.20 there is no s3-cloudformation-template.json to edit and place the s3:PutObjectACL into anymore. there is only a cli-input.json with enums for permissions:
export enum S3StorageParamsPermissionType {
CREATE_AND_UPDATE = 'create/update',
READ = 'read',
DELETE = 'delete',
}
and there is no enum for PutObjectACL so the manual edit doesn't work any more. any other suggestions?
I've managed to set s3:PutObjectAcl
using overrides in Amplify. In a previous version of Amplify, this worked. More recently it hasn't been working.
I've posted this in #10001 but I thought I'd share here because it is related.
My override looks something like this
export function override(resources: AmplifyS3ResourceTemplate) {
resources.s3AuthPublicPolicy.policyDocument.Statement[0] = {
...resources.s3AuthPublicPolicy.policyDocument.Statement[0],
Action: [
"s3:PutObjectAcl",
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject",
],
};
}
This gives a resulting IAM Policy within the authRole IAM role for Public_policy
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"s3:PutObjectAcl",
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject"
],
"Resource": [
"arn:aws:s3:::<BUCKET_NAME_HERE>/public/*"
],
"Effect": "Allow"
}
]
}
Uploading using the Storage Amplify API returns a 403 error while logged into the Amplify app
await Storage.put(fileName, file, {
contentType: file.type,
level: "public",
acl: "public-read",
});
Removing the line acl: "public-read"
results in a successful upload but it means the files are not publicly accessible via a permanent URL.
I've discovered my issue was to do with cognito user groups. Users in user groups assume a different IAM role that is not the same as the authRole. You need to add overrides for your user groups as well if you want users that are in those groups to be able to have the same actions. I added this just above where I overwrote s3AuthPublicPolicy
resources.addCfnResource(
{
type: "AWS::IAM::Policy",
properties: {
PolicyName: "<USER_GROUP_NAME>-group-s3-PutObjectAcl-policy",
PolicyDocument: {
Version: "2012-10-17",
Statement: [
{
Action: ["s3:PutObjectAcl"],
Effect: "Allow",
Resource: {
"Fn::Join": [
"",
[
"arn:aws:s3:::",
{
Ref: "S3Bucket",
},
"/*",
],
],
},
},
],
},
Roles: [
{
"Fn::Join": [
"",
[
{
Ref: "auth<AUTH_RESOURCE_NAME>UserPoolId",
},
"-<USER_GROUP_NAME>Role",
],
],
},
],
},
},
"<USER_GROUP_NAME>PutObjectAclPolicy"
);
Although you can do this with overrides, it should really be an option when you apply privileges using the amplify cli.
I've managed to set
s3:PutObjectAcl
using overrides in Amplify. In a previous version of Amplify, this worked. More recently it hasn't been working.I've posted this in #10001 but I thought I'd share here because it is related.
My override looks something like this
export function override(resources: AmplifyS3ResourceTemplate) { resources.s3AuthPublicPolicy.policyDocument.Statement[0] = { ...resources.s3AuthPublicPolicy.policyDocument.Statement[0], Action: [ "s3:PutObjectAcl", "s3:PutObject", "s3:GetObject", "s3:DeleteObject", ], }; }
This gives a resulting IAM Policy within the authRole IAM role for Public_policy
{ "Version": "2012-10-17", "Statement": [ { "Action": [ "s3:PutObjectAcl", "s3:PutObject", "s3:GetObject", "s3:DeleteObject" ], "Resource": [ "arn:aws:s3:::<BUCKET_NAME_HERE>/public/*" ], "Effect": "Allow" } ] }
Uploading using the Storage Amplify API returns a 403 error while logged into the Amplify app
await Storage.put(fileName, file, { contentType: file.type, level: "public", acl: "public-read", });
Removing the line
acl: "public-read"
results in a successful upload but it means the files are not publicly accessible via a permanent URL.
I was able to do that by overriding with this:
import {AmplifyS3ResourceTemplate} from '@aws-amplify/cli-extensibility-helper';
export function override(resources: AmplifyS3ResourceTemplate) {
const policyDoc = JSON.parse(
JSON.stringify(resources.s3AuthPublicPolicy.policyDocument, null, 2),
);
const newPolicyDoc = {...policyDoc};
const newActions = [
's3:PutObject',
's3:GetObject',
's3:DeleteObject',
's3:PutObjectAcl', // allow to users to set public objects
];
newPolicyDoc.Statement.Action = newActions;
resources.s3AuthPublicPolicy.policyDocument.Statement.Action = newActions;
}
Previously (at least Amplify CLI 6.3.1) the public_policy was set to "s3: PutObjectAcl", "s3: PutObject", "s3: GetObject", "s3: DeleteObject". When I pushed with Amplify CLI 8.2.0, there was only "s3: PutObject", and it became 403 when accessing the resource.
Please let me know if anyone knows how this destructive change came about. thanks.
You should overwrite your s3 policy.
amplify override storage
Then copy this:
import {AmplifyS3ResourceTemplate} from '@aws-amplify/cli-extensibility-helper';
export function override(resources: AmplifyS3ResourceTemplate) {
const policyDoc = JSON.parse(
JSON.stringify(resources.s3AuthPublicPolicy.policyDocument, null, 2),
);
const newPolicyDoc = {...policyDoc};
const newActions = [
's3:PutObject',
's3:GetObject',
's3:DeleteObject',
's3:PutObjectAcl', // allow to users to set public objects
];
newPolicyDoc.Statement.Action = newActions;
resources.s3AuthPublicPolicy.policyDocument.Statement.Action = newActions;
}
and paste that to overwrite.ts in storage folder
@alphonse92
thanks. but override is not working. Even though "amplify push " after it, it remains putobjet.
Until now, there was no need to override. In another env, the original public role is created without overriding.
I am also having this issue. No matter how I setup my storage. The objects in public are not being set to public readable. This is very frustrating. Is there a fix for this?
Is your feature request related to a problem? Please describe. Currently when setting authenticated access to storage, you're given the option to set put, read, delete access for both auth and unauth access. The aws-amplify package allows passing an
acl: public-read
(see "other options"). When you try to set this with the default write permissions during setup, you receive a 403 error because the role isn't set up withPutObjectAcl
- you have to manually add the permission to thes3PermissionsAuthenticatedPublic
parameter tostorage/parameters.json
before pushing again.Pretty common use case to upload photos that are publicly accessible, so this seems like a no-brainer.
Describe the solution you'd like Add a "Grant public access" option when specifying auth permissions during setup.
Describe alternatives you've considered Manually adding the permission to the parameters is easy, however, might trip up inexperienced developers who (rightly) expect the
aws-amplify/Storage.put
option 'acl: public-read' to work out of the box.