Search code examples
pythonreactjsreduxboto3saga

Signing an URL to upload an object with Python Boto3+DjangoRest+React Redux Saga


Im trying to sign a url using boto3 like this:

 @list_route(methods=['get'])
    def sign(self, request):
        key = request.GET.get('key', None)
        if key:
           if key.count('.') == 1 and key.count('/') == 1:
               extension = key.split('.')
               if '*' in settings.ALLOWED_FILE_TYPES or extension.lower() in settings.ALLOWED_FILE_TYPES:
                   # Get the service client
                   s3 = boto3.client('s3')
                   # Generate the POST attributes
                   post = s3.generate_presigned_url(
                       'put_object',
                       {'Bucket': settings.FILE_BUCKET, 'Key': 'tmp/{}'.format(key)},
                       300,
                       'PUT'
                   )
                   return Response(
                       status=200,
                       data={'presigned_post': post}
                   )
               else:
                   return Response(
                       status=400,
                       data="File Type not Allowed"
                   )
           else:
               return Response(
                   status=400,
                   data="Invalid Key"
               )
        else:
            return Response(
                status=400,
                data='Missing Key'
            )

I can generate the URL and then make the following request to upload the file on a Saga on react:

    // Generate Signed URL
    const resultSignUrl = yield call([File.custom, 'sign'], {token, params:{key: data.key}});
    let signedUrl = resultSignUrl.presigned_post;
    let options = {
        headers: {
            'Content-Type': file.type
        }
    };
    console.log("SIGN URL", resultSignUrl);
    console.log("SIGN URL file", file);

    // Upload to S3
    const resultUpload = yield call([axios, 'put'], signedUrl, file, options);
    console.log("S3 UPLOAD", resultUpload);

The problem is that AWS replies this when I make the request:

<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message><AWSAccessKeyId>"SomeAccessKey"</AWSAccessKeyId><StringToSign>PUT

application/vnd.oasis.opendocument.text
1516318279
/student-tracker-files/tmp/14a09716-8850-47cb-bf3a-7820f42c97d4/test%20honduras.odt</StringToSign><SignatureProvided>EFqdML8OgILC/L/hoJBqmyhuzRk=</SignatureProvided><StringToSignBytes>50 55 54 0a 0a 61 70 70 6c 69 63 61 74 69 6f 6e 2f 76 6e 64 2e 6f 61 73 69 73 2e 6f 70 65 6e 64 6f 63 75 6d 65 6e 74 2e 74 65 78 74 0a 31 35 31 36 33 31 38 32 37 39 0a 2f 73 74 75 64 65 6e 74 2d 74 72 61 63 6b 65 72 2d 66 69 6c 65 73 2f 74 6d 70 2f 31 34 61 30 39 37 31 36 2d 38 38 35 30 2d 34 37 63 62 2d 62 66 33 61 2d 37 38 32 30 66 34 32 63 39 37 64 34 2f 74 65 73 74 25 32 30 68 6f 6e 64 75 72 61 73 2e 6f 64 74</StringToSignBytes><RequestId>193F6C165445A3E4</RequestId><HostId>odoIo4+4sdVUuDJucnrkzeyJJUYJ5sMDqpS7DAzV3nJvSrN4Eu+7b1Srs2FEQy2aDHuYEakMBGY=</HostId></Error>

Can anyone tell me what I might be doing wrong? I have changed the acces key and secret and still get the same response. I have read the docs on boto3 but I see no difference between my code and the one they show in their examples.


Solution

  • You should try to add content type to the django boto function

    ost = s3.generate_presigned_url(
             'put_object',
             {'Bucket': settings.FILE_BUCKET,
             'ContentType': request.GET.get('type'),
             'Key': 'tmp/{}'.format(key)},
             3600,
             'PUT'
          )