boto / boto3

AWS SDK for Python
https://aws.amazon.com/sdk-for-python/
Apache License 2.0
9.07k stars 1.87k forks source link

S3 generate_presigned_post does not include success_action_redirect in the policy #4137

Closed martin-adam-sonar closed 4 months ago

martin-adam-sonar commented 5 months ago

Describe the bug

When generating a pre-signed post request to upload a file to S3 and specifying the success_action_redirect parameter, this parameter does not get included into the signed policy document. The following upload then fails with an error.

Expected Behavior

The generated policy will contain the success_action_redirect parameter. This is the behavior of the JS SDK implementation.

Current Behavior

The following code fails the assertion.

Reproduction Steps

In a lambda with Python 3.12 ARM runtime, run.

import boto3
import os
from botocore.config import Config
import base64
import json

def lambda_handler(event, context):
    s3_client = boto3.client(
        "s3", config=Config(region_name=os.environ["AWS_REGION"], signature_version="v4")
    )

    bucket = "some-bucket"
    key = "test"

    signed_post_req = s3_client.generate_presigned_post(
        bucket,
        key,
        Fields={"success_action_redirect": "https://example.com/"},
        Conditions=[["content-length-range", 0, 5 * 1024 * 1024]],  # 5 MB max
    )

    decoded_policy = json.loads(base64.b64decode(signed_post_req["fields"]["policy"]).decode('utf-8'))
    assert "success_action_redirect" in decoded_policy

Updating to the latest version of boto3 does not solve the issue.

Possible Solution

No response

Additional Information/Context

No response

SDK version used

1.34.109 and 1.34.42

Environment details (OS name and version, etc.)

Lambda with Python 3.12 ARM

Neah-Ko commented 5 months ago

+1 to this issue.

I am trying to integrate upload/download of files through pre-signed URLs in a software product.

Here is how I am generating urls:

    def create_presigned_post(self,
                              object_name,
                              callback,
    ):
        """
        From boto3 official doc:
        - https://boto3.amazonaws.com/v1/documentation/api/latest/guide/s3-presigned-urls.html
        """
        fields = {'success_action_redirect': callback}
        conditions = [
            {"acl": "authenticated-read"},
            ["starts-with", "$success_action_redirect", ""]
        ]

        try:
            return self.s3_client.generate_presigned_post(
                Key=object_name,
                Bucket=self.bucket_name,
                Fields=fields,
                Conditions=conditions,
                ExpiresIn=self.url_expiration
            )

and how I'm testing them:

postv4 = {'url': ..., 'fields' ..}

file_path = "/path/to/test.txt"
file_name = "test.txt"

with open(file_path, 'rb') as f:
    files = {'file': (file_name, f)}
    http_response = requests.post(postv4['url'], data=postv4['fields'], files=files)
    print(http_response.status_code)

If I let Fields and Condtions objects in the url generation function call, subsequent request fails with this error:

ConnectionResetError: [Errno 104] Connection reset by peer

If I comment them out, file get successfully uploaded and returns me a status_code=204


Is this the only way of setting a callback on a pre-signed url ? It is kinda essential for my software product in order to receive a signal that those files got populated. As generate_presigned_url is not compatible with passing in Fields and Conditions, I will be stuck when trying to do the same thing on download (it is less essential, but would be nice to have).


EDIT Found a way to pass that. Browsing issues, I've found this one from minio that gives a good example on how to generously populate conditions and fields dictionaries so that your request passes through.

Also when fields contains a success_action_redirect, conditions needs to be expanded with the following policy: ["starts-with", "$success_action_redirect", ""]

That still leaves the question about download. It'd be nice I could hit back my API in order to collect statistics and such. I guess I could try to use the signer like botocore is doing for a presigned_post.

tim-finnigan commented 5 months ago

Hi @martin-adam-sonar thanks for reaching out. Can you share the JS SDK code that does the behavior you expect here? Also can you share more context on your use case? I have seen examples of generate_presigned_post with a success action redirect in a client-side form but not in a Lambda function. Here is an example from the S3 User Guide: https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-post-example.html

github-actions[bot] commented 5 months ago

Greetings! It looks like this issue hasn’t been active in longer than five days. We encourage you to check if this is still an issue in the latest release. In the absence of more information, we will be closing this issue soon. If you find that this is still a problem, please feel free to provide a comment or upvote with a reaction on the initial post to prevent automatic closure. If the issue is already closed, please feel free to open a new one.