diff --git a/changelogs/fragments/318-s3-upload-acl.yml b/changelogs/fragments/318-s3-upload-acl.yml new file mode 100644 index 00000000000..19326ceb315 --- /dev/null +++ b/changelogs/fragments/318-s3-upload-acl.yml @@ -0,0 +1,2 @@ +bugfixes: +- aws_s3 - Fix upload permission when an S3 bucket ACL policy requires a particular canned ACL (https://github.com/ansible-collections/amazon.aws/pull/318) diff --git a/plugins/modules/aws_s3.py b/plugins/modules/aws_s3.py index b0ec5d33895..b0eebaf0ce0 100644 --- a/plugins/modules/aws_s3.py +++ b/plugins/modules/aws_s3.py @@ -78,7 +78,8 @@ - This option lets the user set the canned permissions on the object/bucket that are created. The permissions that can be set are C(private), C(public-read), C(public-read-write), C(authenticated-read) for a bucket or C(private), C(public-read), C(public-read-write), C(aws-exec-read), C(authenticated-read), C(bucket-owner-read), - C(bucket-owner-full-control) for an object. Multiple permissions can be specified as a list. + C(bucket-owner-full-control) for an object. Multiple permissions can be specified as a list; although only the first one + will be used during the initial upload of the file default: ['private'] type: list elements: str @@ -531,6 +532,13 @@ def upload_s3file(module, s3, bucket, obj, expiry, metadata, encrypt, headers, s else: extra['Metadata'][option] = metadata[option] + if module.params.get('permission'): + permissions = module.params['permission'] + if isinstance(permissions, str): + extra['ACL'] = permissions + elif isinstance(permissions, list): + extra['ACL'] = permissions[0] + if 'ContentType' not in extra: content_type = None if src is not None: diff --git a/tests/integration/targets/aws_s3/defaults/main.yml b/tests/integration/targets/aws_s3/defaults/main.yml index eb7dd2d3712..67d026de087 100644 --- a/tests/integration/targets/aws_s3/defaults/main.yml +++ b/tests/integration/targets/aws_s3/defaults/main.yml @@ -1,3 +1,4 @@ --- # defaults file for s3 -bucket_name: '{{resource_prefix}}' +bucket_name: '{{ resource_prefix }}' +bucket_name_acl: '{{ bucket_name }}-with-acl' diff --git a/tests/integration/targets/aws_s3/tasks/main.yml b/tests/integration/targets/aws_s3/tasks/main.yml index 5d811ce1775..e44f80934cc 100644 --- a/tests/integration/targets/aws_s3/tasks/main.yml +++ b/tests/integration/targets/aws_s3/tasks/main.yml @@ -8,6 +8,14 @@ region: "{{ aws_region }}" block: + - name: get ARN of calling user + aws_caller_info: + register: aws_caller_info + + - name: register account id + set_fact: + aws_account: "{{ aws_caller_info.account }}" + - name: Create temporary directory tempfile: state: directory @@ -526,6 +534,47 @@ - result is not changed when: ansible_system == 'Linux' or ansible_distribution == 'MacOSX' + - name: make a bucket with the bucket-owner-full-control ACL + s3_bucket: + name: "{{ bucket_name_acl }}" + state: present + policy: "{{ lookup('template', 'policy.json.j2') }}" + register: bucket_with_policy + + - assert: + that: + - bucket_with_policy is changed + + - name: fail to upload the file to the bucket with an ACL + aws_s3: + bucket: "{{ bucket_name_acl }}" + mode: put + src: "{{ tmpdir.path }}/upload.txt" + object: file-with-permissions.txt + permission: private + ignore_nonexistent_bucket: True + register: upload_private + ignore_errors: True + + # XXX Doesn't fail... + # - assert: + # that: + # - upload_private is failed + + - name: upload the file to the bucket with an ACL + aws_s3: + bucket: "{{ bucket_name_acl }}" + mode: put + src: "{{ tmpdir.path }}/upload.txt" + object: file-with-permissions.txt + permission: bucket-owner-full-control + ignore_nonexistent_bucket: True + register: upload_owner + + - assert: + that: + - upload_owner is changed + - name: create an object from static content aws_s3: bucket: "{{ bucket_name }}" @@ -639,9 +688,22 @@ - delete.txt - delete_encrypt.txt - delete_encrypt_kms.txt + - multipart.txt - put-content.txt - put-template.txt - put-binary.txt + - foo/bar/baz + - foo/bar + - foo + ignore_errors: yes + + - name: remove uploaded files (bucket with ACL) + aws_s3: + bucket: "{{ bucket_name_acl }}" + mode: delobj + object: "{{ item }}" + loop: + - file-with-permissions.txt ignore_errors: yes - name: delete temporary files @@ -661,3 +723,9 @@ bucket: "{{ bucket_name + '.bucket' }}" mode: delete ignore_errors: yes + + - name: delete the acl bucket + aws_s3: + bucket: "{{ bucket_name_acl }}" + mode: delete + ignore_errors: yes diff --git a/tests/integration/targets/aws_s3/templates/policy.json.j2 b/tests/integration/targets/aws_s3/templates/policy.json.j2 new file mode 100644 index 00000000000..4af2e0713b1 --- /dev/null +++ b/tests/integration/targets/aws_s3/templates/policy.json.j2 @@ -0,0 +1,21 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "Only allow writes to my bucket with bucket owner full control", + "Effect": "Allow", + "Principal": { "AWS":"{{ aws_account }}" }, + "Action": [ + "s3:PutObject" + ], + "Resource": [ + "arn:aws:s3:::{{ bucket_name_acl }}/*" + ], + "Condition": { + "StringEquals": { + "s3:x-amz-acl": "bucket-owner-full-control" + } + } + } + ] +}