Difficulty passing key when creating server using Springloops API - springloops

I am trying to use the Springloops API to create a new deployment server but am having difficulty passing the key.
When I try the full key:
curl --header "Accept: application/json" --header "Content-type: application/json" \
--header "Authorization: Basic a2V2aW5AdGh*****************************Mzg1eGlrYnRUTQ==" \
--data-binary '{"name": "testDeploy1",
"role": "DEVELOPMENT",
"projectPathInRepository": "/",
"protocol": "SFTP_KEY",
"branch": "develop",
"isAutoDeployment": false,
"host": "148.251.124.123",
"port": "22",
"path": "/home/httpd/develop.example.co.uk",
"isPathAbsolute": true,
"privateKey": "-----BEGIN RSA PRIVATE KEY-----
MIIEoQIBAAKCAQEAm+hBiQ4W88nAG+ri0+ogfvN/ZF0NV7VTyl/+OcsWrBFZFM0A
1XMQJaQnJguWH+iHtpNvghf+QQlP+ln9ndk9W8OEGrSi75q2WWE6O53wz3+vS1Yj
ium5gFeSOuAQGRGkwqHsMI20trkNSgJAUhaqiVaG+SONRaYIDJfMO2+ZrRqy/oIB
****************************************************************
****************************************************************
... snip ...
****************************************************************
****************************************************************
rlOW7b39DrojA98xr6ltAoGAChUFYB7L5C1032DOd5QmP7rqKggugrwT4qC0Sx8C
zFeB/hDPHRPKfhE2tpJRiR6O8cOulEqoTWKNJAHspfHozv5YuZ5sCYsyQk+FpX9p
gDnWky0LTslb3np7mLH5cHwQmjTCupOVR8S9ydqtDWBaPyUlfqEl9M97eaNKS0wg
k50CgYB4aZmf3vEoIUWxwVvVhLEdJvxjQFeMjjr4aUBFglOY3sZjrFzdkEBrHNYm
Rl3zAUggdobo7UfEZpBAsUUFufaym8uPLrhVYssL1qOAvbc57i+QAabemuIgX40h
zr/dIDiWam+RafEDoLnnZhq9nhVcBa98qSFj/Yf/SBH+3NwlZg==
-----END RSA PRIVATE KEY-----"}' \
https://example.springloops.io/api/project/180346/servers
I get the response
{"success":false,"code":400,"message":"Please provide server name"}
When I do the following CURL with an obviously bogus ssh key:
curl --header "Accept: application/json" --header "Content-type: application/json" \
--header "Authorization: Basic a2V2a****************zg1eGlrYnRUTQ==" \
--data-binary '{"name": "testDeploy1",
"role": "DEVELOPMENT",
"projectPathInRepository": "/",
"protocol": "SFTP_KEY",
"branch": "develop",
"isAutoDeployment": false,
"host": "148.251.124.123",
"port": "22",
"path": "/home/httpd/develop.example.co.uk",
"isPathAbsolute": true,
"privateKey": "PrivateKeyHere"}' \
https://example.springloops.io/api/project/180346/servers
I get the response
{"success":false,"code":0,"message":"Error while connecting to the server: The key format is not a supported format"}
Which is what I would expect. If I go back to the bogus key but add a second line
curl --header "Accept: application/json" --header "Content-type: application/json" \
--header "Authorization: Basic a2V2a****************zg1eGlrYnRUTQ==" \
--data-binary '{"name": "testDeploy1",
"role": "DEVELOPMENT",
"projectPathInRepository": "/",
"protocol": "SFTP_KEY",
"branch": "develop",
"isAutoDeployment": false,
"host": "148.251.124.123",
"port": "22",
"path": "/home/httpd/develop.example.co.uk",
"isPathAbsolute": true,
"privateKey": "PrivateKeyHere
SecondLine"}' \
https://example.springloops.io/api/project/180346/servers
I get the same failed response again:
{"success":false,"code":400,"message":"Please provide server name"}
So it appears to be something with the multi-line parameter in the json.
Using RequestBin, It appears that the data is being passed OK - it is being received as
{"name": "testDeploy1",
"role": "DEVELOPMENT",
"projectPathInRepository": "/",
"protocol": "SFTP_KEY",
"branch": "develop",
"isAutoDeployment": false,
"host": "148.251.124.123",
"port": "22",
"path": "/home/httpd/develop.example.co.uk",
"isPathAbsolute": true,
"privateKey": "-----BEGIN RSA PRIVATE KEY-----
MIIEoQIBAAKCAQEAm+hBiQ4W88nAG+ri0+ogfvN/ZF0NV7VTyl/+OcsWrBFZFM0A
1XMQJaQnJguWH+iHtpNvghf+QQlP+ln9ndk9W8OEGrSi75q2WWE6O53wz3+vS1Yj
ium5gFeSOuAQGRGkwqHsMI20trkNSgJAUhaqiVaG+SONRaYIDJfMO2+ZrRqy/oIB
****************************************************************
****************************************************************
****************************************************************
****************************************************************
rlOW7b39DrojA98xr6ltAoGAChUFYB7L5C1032DOd5QmP7rqKggugrwT4qC0Sx8C
zFeB/hDPHRPKfhE2tpJRiR6O8cOulEqoTWKNJAHspfHozv5YuZ5sCYsyQk+FpX9p
gDnWky0LTslb3np7mLH5cHwQmjTCupOVR8S9ydqtDWBaPyUlfqEl9M97eaNKS0wg
k50CgYB4aZmf3vEoIUWxwVvVhLEdJvxjQFeMjjr4aUBFglOY3sZjrFzdkEBrHNYm
Rl3zAUggdobo7UfEZpBAsUUFufaym8uPLrhVYssL1qOAvbc57i+QAabemuIgX40h
zr/dIDiWam+RafEDoLnnZhq9nhVcBa98qSFj/Yf/SBH+3NwlZg==
-----END RSA PRIVATE KEY-----"}
Has anybody had any success with this and can shed some light on it?

The answer is to pass the key on a single line, but use \r\n as line terminators. So the correct method is
curl --header "Accept: application/json" --header "Content-type: application/json" \
--header "Authorization: Basic a2V2aW5AdGh*****************************Mzg1eGlrYnRUTQ==" \
--data-binary '{"name": "testDeploy1",
"role": "DEVELOPMENT",
"projectPathInRepository": "/",
"protocol": "SFTP_KEY",
"branch": "develop",
"isAutoDeployment": false,
"host": "148.251.124.123",
"port": "22",
"path": "/home/httpd/develop.example.co.uk",
"isPathAbsolute": true,
"privateKey": "-----BEGIN RSA PRIVATE KEY-----\r\nMIIEoQIBAAKCAQEAm+hBiQ4W88nAG+ri0+ogfvN/ZF0NV7VTyl/+OcsWrBFZFM0A\r\n1XMQJaQnJguWH+iHtpNvghf+QQlP+ln9ndk9W8OEGrSi75q2WWE6O53wz3+vS1Yj\r\nium5gFeSOuAQGRGkwqHsMI20trkNSgJAUhaqiVaG+SONRaYIDJfMO2+ZrRqy/oIB\r\n****************************************************************\r\n****************************************************************\r\n ... snip ... \r\n****************************************************************\r\n ****************************************************************\r\nrlOW7b39DrojA98xr6ltAoGAChUFYB7L5C1032DOd5QmP7rqKggugrwT4qC0Sx8C\r\nzFeB/hDPHRPKfhE2tpJRiR6O8cOulEqoTWKNJAHspfHozv5YuZ5sCYsyQk+FpX9p\r\ngDnWky0LTslb3np7mLH5cHwQmjTCupOVR8S9ydqtDWBaPyUlfqEl9M97eaNKS0wg\r\nk50CgYB4aZmf3vEoIUWxwVvVhLEdJvxjQFeMjjr4aUBFglOY3sZjrFzdkEBrHNYm\r\nRl3zAUggdobo7UfEZpBAsUUFufaym8uPLrhVYssL1qOAvbc57i+QAabemuIgX40h\r\nzr/dIDiWam+RafEDoLnnZhq9nhVcBa98qSFj/Yf/SBH+3NwlZg==\r\n-----END RSA PRIVATE KEY-----"}' \
https://example.springloops.io/api/project/180346/servers

Related

Merge two arrays and overwrite values | Dataweave

I have situation to merge global array with another custom array or if the name is the same use custom values.
Global:
{
"connections": [
{
"name": "Test SFTP",
"type": "SFTP",
"user": "sftpuser",
"password": "password",
"server": "127.0.0.1",
"port": 22,
},
{
"name": "Test FTP",
"type": "FTP",
"user": "ftpuser",
"password": "password",
"server": "127.0.0.1",
"port": 21,
}
]
}
Custom:
{
"connections": [
{
"name": "Test SFTP",
"user": "sftpuser1",
"password": "password1",
"server": "127.0.0.2",
},
{
"name": "Test FTPS",
"type": "FTPS",
"user": "ftpsuser",
"password": "password",
"server": "127.0.0.1",
"port": 990,
}
]
}
Expected:
{
"connections": [
{
"name": "Test SFTP",
"type": "SFTP",
"user": "sftpuser1",
"password": "password1",
"server": "127.0.0.2",
"port": 22,
},
{
"name": "Test FTP",
"type": "FTP",
"user": "ftpuser",
"password": "password",
"server": "127.0.0.1",
"port": 21,
},
{
"name": "Test FTPS",
"type": "FTPS",
"user": "ftpsuser",
"password": "password",
"server": "127.0.0.1",
"port": 990,
}
]
}
Global will always have all fields but custom can have name + just one field to override global.
Later on I will validate if the json is ok but for now i just need to merge and overwrite.
Thanks,
Ivan
Below script will help you.
%dw 2.0
output application/json
import mergeWith from dw::core::Objects
import * from dw::core::Arrays
var global = {
"connections": [
{
"name": "Test SFTP",
"type": "SFTP",
"user": "sftpuser",
"password": "password",
"server": "127.0.0.1",
"port": 22,
},
{
"name": "Test FTP",
"type": "FTP",
"user": "ftpuser",
"password": "password",
"server": "127.0.0.1",
"port": 21,
}
]
}
var custom = {
"connections": [
{
"name": "Test SFTP",
"user": "sftpuser1",
"password": "password1",
"server": "127.0.0.2",
},
{
"name": "Test FTPS",
"type": "FTPS",
"user": "ftpsuser",
"password": "password",
"server": "127.0.0.1",
"port": 990,
}
]
}
---
outerJoin(global.connections, custom.connections, (g) -> g.name, (c) -> c.name) map ($.l mergeWith $.r )

Firebase - Request header field x-firebase-gmpid is not allowed by Access-Control-Allow-Headers in preflight response

My product uses firebase rtdb, firestore, storage, auth, and hosting.
I didn't make any changes to my CORS configuration. However, today I started getting the following CORS error while trying to upload images to storage, and retrieve them:
Access to XMLHttpRequest at 'https://firebasestorage.googleapis.com/v0/b/diary-a77f6.appspot.com/o?name=images%2FJ1gU3KPfo1cTHJXhT3iopBqrvVs1%2F-M1Ah4xCQ46GvEZtwgR1%2FBalboa%20Pier%2C%20California%20-%201600x1200%20-%20ID%2027253.jpg' from origin 'https://daybook.app' has been blocked by CORS policy: Request header field x-firebase-gmpid is not allowed by Access-Control-Allow-Headers in preflight response.
I tried Firebase Storage and Access-Control-Allow-Origin,
https://cloud.google.com/storage/docs/configuring-cors, and https://firebase.google.com/docs/storage/web/download-files#cors_configuration to no avail.
My hosting config:
{
"target": "prod",
"public": "build/production",
"ignore": [
"firebase.json",
"src/firebase/keys.js",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
],
"headers": [
{
"source": "/**",
"headers": [
{
"key": "Cache-Control",
"value": "no-cache, no-store, must-revalidate"
},
{
"key": "Access-Control-Allow-Headers",
"value": "x-firebase-gmpid, Origin, Accept, Content-Type, X-Requested-With, Access-Control-Request-Method,Access-Control-Request-Headers, Authorization"
}
]
},
{
"source": "**/*.#(css|js)",
"headers": [
{
"key": "Cache-Control",
"value": "no-cache, no-store, must-revalidate max-age=0"
}
]
},
{
"source": "**/*.#(eot|otf|ttf|ttc|woff|font.css)",
"headers": [
{
"key": "Access-Control-Allow-Origin",
"value": "*"
}
]
},
{
"source": "**/*.#(jpg|jpeg|gif|png|webp|webp|svg)",
"headers": [
{
"key": "Cache-Control",
"value": "max-age=7200"
}
]
}
]
}
This is what I added recently while trying to fix the issue:
{
"key": "Access-Control-Allow-Headers",
"value": "x-firebase-gmpid, Origin, Accept, Content-Type, X-Requested-With, Access-Control-Request-Method,Access-Control-Request-Headers, Authorization"
}
I think this was an issue with the Firestore JS client library. I rolled it back to version 7.9.0 from version 7.9.2 and the error went away.
edit: 7.9.3 contains the fix.

DocuSign Request error "Envelope definition missing."

I am trying to make 'createAndSendEnvelope' request using DocuSign REST api/v2. My JSON is valid.
I came across the same question and I tried the solution given in there i.e. I made sure all new lines use '\r\n'.
What am I missing here? Here is my request, I get error code, "message": "The Envelope is not Complete. A Complete Envelope Requires Documents, Recipients, Tabs, and a Subject Line. Envelope definition missing."
POST https://demo.docusign.net/restapi/v2/accounts/xxxxx/envelopes HTTP/1.1
X-DocuSign-Authentication: {"Username":"xxxxxx","Password":"xxxxx","IntegratorKey":"xxxxxx"}
Content-Type: multipart/form-data; boundary=AAA
Accept: application/json
Host: demo.docusign.net
Content-Length: 90500
Expect: 100-continue
--AAA
Content-Type: application/json
Content-Disposition: form-data
{
"status": "sent",
"recipients": {
"signers": [{
"tabs": {
"signHeretabs": [{
"yPosition": "15",
"xPosition": "249",
"width": "100",
"tablabel": null,
"required": "TRUE",
"recipientId": "1",
"pagenumber": "1",
"fontSize": "Size12",
"font": "Calibri",
"documentid": "1",
"anchorYoffset": null,
"anchorXOffset": null,
"anchorUnits": "pixels",
"anchorString": "Sign Here",
"anchorIgnoreIfNotPresent": "true"
}],
"initialsTabs": [{
"yPosition": "45",
"xPosition": "249",
"width": "100",
"tablabel": null,
"required": "TRUE",
"receipientId": "1",
"pagenumber": "1",
"fontSize": "Size12",
"font": "Calibri",
"documentId": "1",
"anchorYoffset": null,
"anchorXOffset": null,
"anchorUnits": "pixels",
"anchorString": "Initials Here",
"anchorIgnoreIfNotPresent": "true"
}],
"dateSignedTabs": [{
"yPosition": "65",
"xPosition": "249",
"width": "100",
"tablabel": null,
"required": "TRUE",
"receipientid": "1",
"pagenumber": "1",
"fontSize": "Size12",
"font": "Calibri",
"documentid": "1",
"anchorYoffset": "-5",
"anchorXOffset": null,
"anchorUnits": "pixels",
"anchorString": "Date Signed",
"anchorIgnoreIfNotPresent": "true"
}]
},
"routingorder": "1",
"rolename": "roleNameHere",
"recipientid": "1",
"name": "XXXXXXXXXX",
"email": "xxxxx#gcpa.com"
}]
},
"emailsubject": "TestDocuSign Call",
"emailBlurb": "Test Email Blurb",
"documents": [{
"name": "Name1",
"fileExtension": ".pdf",
"documentId": "1",
"documentBase64": null
}]
}
--AAA
Content-Type: application/octet-stream
Content-Disposition: file; filename="Name1.pdf"; documentid="1"
Content-Transfer-Encoding: base64
VGhpcyBpcyBh
You can specify your base64 encoded document bytes in the documentBase64 property
POST https://demo.docusign.net/restapi/v2/accounts/xxxxx/envelopes HTTP/1.1
X-DocuSign-Authentication: {"Username":"xxxxxx","Password":"xxxxx","IntegratorKey":"xxxxxx"}
Content-Type: application/json
{
"status": "sent",
"recipients": {
"signers": [{
"tabs": {
<Removed tabs for brevity>
},
"routingorder": "1",
"rolename": "roleNameHere",
"recipientid": "1",
"name": "XXXXXXXXXX",
"email": "xxxxx#gcpa.com"
}]
},
"emailsubject": "TestDocuSign Call",
"emailBlurb": "Test Email Blurb",
"documents": [{
"name": "Name1",
"fileExtension": ".pdf",
"documentId": "1",
"documentBase64": "VGhpcyBpcyBh"
}]
}

How to avoid loading attachments data from Gmail Rest API get message

How to avoid loading attachments data from Gmail Rest API with get message request.
Using the fields parameter at least we can avoid loading few fields but when I want to load message body, attachment data also coming along with body just like IMAP
You don't get the attachment like you do in IMAP. You get an attachmentId which you have to use in an additional request to get the attachment.
I just sent a message with an attached image to myself. This is what the response from the API looks like:
{
"id": "1573ec1aa0976b42",
"threadId": "1573ec1aa0976b42",
"labelIds": [
"SENT",
"INBOX",
"IMPORTANT",
"UNREAD"
],
"snippet": "",
"historyId": "939514",
"internalDate": "1474226662000",
"payload": {
"mimeType": "multipart/related",
"filename": "",
"headers": [ ... ],
"body": {
"size": 0
},
"parts": [
{
"mimeType": "multipart/alternative",
"filename": "",
"headers": [
{
"name": "Content-Type",
"value": "multipart/alternative; boundary=94eb2c0d3cba8637a2053ccd2461"
}
],
"body": {
"size": 0
},
"parts": [
{ ... },
{
"partId": "1",
"mimeType": "image/png",
"filename": "Screen Shot",
"headers": [ ... ],
"body": {
"attachmentId": "ANGjdJ-bmCvsIaV-4KfALXzVV_D567w4i6ksLnwIZhLAl3VXCE335663UbmOLC_vbLrCFusNtnWVpdTv3i88uR482kFwLZqAcwmI7C5gFlamob2aK4-lqAPlCZs17jtCQR9y5Mt4nnpP_Kg64N9qgXbDF0E2vYnEw4xwtEKEo4fRIAbc94ZjjfynFD832mh1B37XFMt-bYw9wkNv24xBife0koBNYpKs-gGLJkfu2EoZouqunGDX9ry1jq2jW2AClWcFXPXvgRBMjUcoRDPtvb9LLLrBhDjU1hu6r1Ibc3c2BSoBogT8QyIp2VUCuFU",
"size": 1511996
}
}
]
},
"sizeEstimate": 1513185
}
As you can see, it just contains an attachmentId and no actual attachment data.

Which Actions do I need to specify in user policy so I can upload files to S3?

If I assign an AdministratorAccess policy to my S3 user then I can upload files from my web app to AWS S3 easily.
Policy name: AdministatorAccess
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "*",
"Resource": "*"
}
]
}
But when I try to limit his privileges via another policy I receive a 403 error from Amazon - AccessDenied.
Request headers:
Remote Address: [hidden]
Request URL:https://mydevelopmentbucket.s3-us-west-2.amazonaws.com/
Request Method:POST
Status Code:403 Forbidden
Returned xml from Amazon S3:
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>AccessDenied</Code>
<Message>Access Denied</Message>
<RequestId>B9DDXX267F8E201E</RequestId>
<HostId>gAU8sdlfkjsflkjsdZEmFT0VJwOG3FYdflkjdsfx6Po=</HostId>
</Error>
Are not these actions enough for file uploading? Below is modified (limited) user policy.
"s3:DeleteObject",
"s3:GetObject",
"s3:PutObject",
"s3:ListBucket"
limited_user policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowUploadingInProduction",
"Effect": "Allow",
"Action": [
"s3:DeleteObject",
"s3:GetObject",
"s3:PutObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::myproductionbucket/*"
]
},
{
"Sid": "AllowUploadingInDevelopment",
"Effect": "Allow",
"Action": [
"s3:DeleteObject",
"s3:GetObject",
"s3:PutObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::mydevelopmentbucket/*"
]
}
]
}
Development bucket policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "UploadFile",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::5503214313988:user/limited_user"
},
"Action": [
"s3:DeleteObject",
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::mydevelopmentbucket/*"
},
{
"Sid": "ListBucket",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::5503214313988:user/limited_user"
},
"Action": [
"s3:ListBucket"
],
"Resource": "arn:aws:s3:::mydevelopmentbucket"
},
{
"Sid": "crossdomainAccess",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::mydevelopmentbucket/crossdomain.xml"
}
]
}
Request Payload
------WebKitFormBoundaryGGlyxVetpT9vWBGi
Content-Disposition: form-data; name="key"
3c23688b16c03b7491508ab97595b74ebd301ca6a4f0aaea74a23a81944e457c/avatars/gjRyRE20LzJsGHAwulI1QZqV77JpnPGmTLKrxvvnIpQSqe800zcHT8vvWGF0wVoC/cache2.jpg
------WebKitFormBoundaryGGlyxVetpT9vWBGi
Content-Disposition: form-data; name="AWSAccessKeyId"
AKIAIITCEYZCTQBJ4RUQ
------WebKitFormBoundaryGGlyxVetpT9vWBGi
Content-Disposition: form-data; name="acl"
public-read
------WebKitFormBoundaryGGlyxVetpT9vWBGi
Content-Disposition: form-data; name="policy"
ewogICAgImV4cGlyYXRpb24iOiAiMjAyMC0wMS0wMVQwMDowMDowMFoiLAogICAgImNvbmRpdGlvbnMiOiBbCiAgICAgICAgeyJidsdkfjsflksdjflksdfjHMtd2l0aCIsICIkQ29udGVudC1UeXBlIiwgIiJdLAogICAgICAgIFsic3RhcnRzLXdpdGgiLCAiJGZpbGVuYW1lIiwgIiJdLAogICAgICAgIHsic3VjY2Vzc19hY3Rpb25fc3RhdHVzIjogIjIwMSJ9LAogICAgICAgIFsiY29udGVudC1sZW5ndGgtcmFuZ2UiLCAwLCA1MjQyODgwMDBdCiAgICBdCn0=
------WebKitFormBoundaryGGlyxVetpT9vWBGi
Content-Disposition: form-data; name="signature"
svw7geEWRWER88ERLaxNiIY=
------WebKitFormBoundaryGGlyxVetpT9vWBGi
Content-Disposition: form-data; name="Content-Type"
image/jpeg
------WebKitFormBoundaryGGlyxVetpT9vWBGi
Content-Disposition: form-data; name="filename"
cache2.jpg
------WebKitFormBoundaryGGlyxVetpT9vWBGi
Content-Disposition: form-data; name="success_action_status"
201
------WebKitFormBoundaryGGlyxVetpT9vWBGi
Content-Disposition: form-data; name="file"; filename="undefined"
Content-Type: image/png
------WebKitFormBoundaryGGlyxVetpT9vWBGi--
My angular directive:
$scope.upload = function(dataUrl) {
Upload.upload({
url: '<%= ENV["S3_UPLOAD_URL"] %>',
method: 'POST',
data: {
key: 'avatars/' + $scope.picFile.name,
AWSAccessKeyId: '<%= ENV["AWS_ACCESS_KEY_ID"] %>',
acl: 'public-read',
policy: $scope.policy,
signature: $scope.signature,
"Content-Type": $scope.picFile.type != '' ? $scope.picFile.type : 'application/octet-stream',
filename: $scope.picFile.name,
success_action_status: 201,
file: Upload.dataUrltoBlob(dataUrl)
}
})
.then(
function (resp) {
console.log('Success');
},
function(resp) {
console.log('Error');
},
function(evt) {
$scope.progressPercentage = parseInt(100.0 * evt.loaded / evt.total);
}
);
};
I needed to add 2 more actions to my limited_user policy:
"s3:GetObjectAcl",
"s3:PutObjectAcl"

Resources