I have a Lambda Function that it is accessible by an API Gateway. I can handle all POST and GET submitted requests to API endpoint (https://XXXXXXX.execute-api.us-east-1.amazonaws.com/default/myapi) inside my Lambda, but I need to use some segments at end of my URL when I am using PUT requests.
My Python code to call the API is here and it is working correctly:
import requests
import json
url = 'https://XXXXXXX.execute-api.us-east-1.amazonaws.com/default/myapi'
token = "my token"
data = {
"first_name": "Reza",
"birthday": "1986-09-12"
}
headers = {"Content-Type" : "application/json", "x-api-key":"MY_API_KEY"}
response = requests.put(url, data=json.dumps(data), headers=headers)
print(response.text)
But if I add users segment to end of the URL like this:
url = 'https://XXXXXXX.execute-api.us-east-1.amazonaws.com/default/myapi/users'
it will show this error:
{"message":"Missing Authentication Token"}
I need to add some static segments like users to return the list of all users and some dynamic segments like users/USER_ID (when USER_ID is a dynamic number) to return the information for a special user.
can you please guide me how I can use segmented URL in my AWS API Gateway?
The term you are using segmented URL might have caused your confusion. It is called path parameters with AWS. There is more than one way to do it. ANY+ integration is the easiest to handle.
Integrate with ANY+ integration to your lambda and you are good to go. All the path parameters will be delivered to your lambda.
http://www.1strategy.com/blog/2017/06/06/how-to-use-amazon-api-gateway-proxy/
Additional path parameter documentation,
https://docs.aws.amazon.com/apigateway/latest/developerguide/integrating-api-with-aws-services-lambda.html#api-as-lambda-proxy-expose-get-method-with-path-parameters-to-call-lambda-function
Good luck.
Related
I am currently creating a basic app with React-Native (frontend) and Flask/MongoDB (backend). I am planning on using AWS S3 as cheap cloud storage for all the images and videos that are going to be uploaded and viewed. My current idea (and this could be totally off), is when a user uploads content, it will go through my Flask API and then to the S3 storage. When a user wants to view content, I am not sure what the plan of attack is here. Should I use my Flask API as a proxy, or is there a way to simply send a link to the content directly on S3 (which would avoid the extra traffic through my API)?
I am quite new to using AWS and if there is already a post discussing this topic, please let me know, and I'd be more than happy to take down this duplicate. I just can't seem to find anything.
Should I use my Flask API as a proxy, or is there a way to simply send a link to the content directly on S3 (which would avoid the extra traffic through my API)?
If the content is public, you just provide an URL which points directly to the file on the S3 bucket.
If the content is private, you generate presigned url on your backend for the file for which you want to give access. This URL should be valid for a short amount of time (for example: 15/30 minutes). You can regenerate it, if it becomes unavailable.
Moreover, you can generate a presigned URL which can be used for uploads directly from the front-end to the S3 bucket. This might be an option if you don't want the upload traffic to go through the backend or you want faster uploads.
There is an API boto3, try to use it.
It is not so difficult, I have done something similar, will post code here.
I have done like #Ervin said.
frontend asks backend to generate credentials
backend sends to frontend the credentials
Frontend upload file to S3
Frontend warns backend it has done.
Backend validate if everything is ok.
Backend will create a link to download, you have a lot of security options.
example of item 6) To generate a presigned url to download content.
bucket = app.config.get('BOTO3_BUCKET', None)
client = boto_flask.clients.get('s3')
params = {}
params['Bucket'] = bucket
params['Key'] = attachment_model.s3_filename
params['ResponseContentDisposition'] = 'attachment; filename={0}'.format(attachment_model.filename)
if attachment_model.mimetype is not None:
params['ResponseContentType'] = attachment_model.mimetype
url = client.generate_presigned_url('get_object', ExpiresIn=3600, Params=params)
example of item 2) Backend will create presigned credentials to post your file on S3, send s3_credentials to frontend
acl_permission = 'private' if private_attachment else 'public-read'
condition = [{'acl': acl_permission},
["starts-with", "$key", '{0}/'.format(folder_name)],
{'Content-Type': mimetype }]
bucket = app.config.get('BOTO3_BUCKET', None)
fields = {"acl": acl_permission, 'Bucket': bucket, 'Content-Type': mimetype}
client = boto_flask.clients.get('s3')
s3_credentials = client.generate_presigned_post(bucket, s3_filename, Fields=fields, Conditions=condition, ExpiresIn=3600)
example of item 5) Here are an example how backend can check if file on S3 are ok.
bucket = app.config.get('BOTO3_BUCKET', None)
client = boto_flask.clients.get('s3')
response = client.head_object(Bucket=bucket, Key=s3_filename)
if response is None:
return None, None
md5 = response.get('ETag').replace('"', '')
size = response.get('ContentLength')
Here are an example how frontend will ask for credentials, upload file to S3 and inform backend it is done.
I tried to remove a lot of particular code.
//frontend asking backend to create credentials, frontend will send some file metadata
AttachmentService.createPostUrl(payload).then((responseCredentials) => {
let form = new FormData();
Object.keys(responseCredentials.s3.fields).forEach(key => {
form.append(key, responseCredentials.s3.fields[key]);
});
form.append("file", file);
let payload = {
data: form,
url: responseCredentials.s3.url
}
//Frontend will send file to S3
axios.post(payload.url, payload.data).then((res) => {
return Promise.resolve(true);
}).then((result) => {
//when it is done, frontend informs backend
AttachmentService.uploadSuccess(...).then((refreshCase) => {
//Success
});
});
});
I am trying to setup authentication in flask-restplus application. I want to add authentication to all endpoints in the application but don't want to write decorator on each route.
I am looking for apikey based authentication. The problem is, I am unable to identify how to intercept all requests and check for authentication token in the header.
Current Code:
authorization = {
'apikey': {
'type': 'apiKey',
'in': 'header',
'name': 'x-auth'
}
}
api = Api(
title='title',
version='1.0',
description="List of API's ",
validate=True,
authorizations=authorization,
security='apikey'
)
After doing the above steps, when I open swagger I can add token using the authorize button. But once the token is passed I am unable to intercept request & verify if token is correct or not.
Currently all the examples I could find, added another decorator on each route which I don't want as it leads to poor design & duplicate code.
Currently the closest example I got is :https://www.youtube.com/watch?v=xF30i_A6cRw&list=LLpaDwEA6bAAPZU5lz0ZRsuw&index=1
but it also uses decorator on each route.
So the problem statement is:
How to intercept all requests & check for correct token in there header without adding decorator on all routes
Very recently, I ran into a similar problem. But luckily we do have the Namespace that accepts a list of decorators, where in you can pass the custom decorator at Resource level, and it will be implemented by default to each method of that resource.
api = Namespace(
'some Name here',
description='some description',
security='apiKey',
authorizations = authorizations,
decorators= [token_required]
)
One point to note however, I had to just specify the security with each doc in the method, as under:
#api.doc('some operation', security = 'apiKey')
The beauty with this is that one click authorization flows to each method in the resource.
There are a few examples for the way to pre-sign the URL of an S3 request, but I couldn't find any working example to pre-sign other services in AWS.
I'm trying to write an item to DynamoDB using the Python SDK botos. The SDK included the option to generate the pre-signed URL here. I'm trying to make it work and I'm getting a URL, but the URL is responding with 404 and the Item is not appearing in the DynamoDB table.
import json
ddb_client = boto3.client('dynamodb')
response = ddb_client.put_item(
TableName='mutes',
Item={
'email': {'S':'g#g.c'},
'until': {'N': '123'}
}
)
print("PutItem succeeded:")
print(json.dumps(response, indent=4))
This code is working directly. But when I try to presign it:
ddb_client = boto3.client('dynamodb')
params = {
'TableName':'mutes',
'Item':
{
'email': {'S':'g#g.c'},
'until' : {'N': '1234'}
}
}
response = ddb_client.generate_presigned_url('put_item', Params = params)
and check the URL:
import requests
r = requests.post(response)
r
I'm getting: Response [404]
Any hint on how to get it working? I checked the IAM permissions, and they are giving full access to DynamoDB.
Please note that you can sign a request to DynamoDB using python, as you can see here: https://docs.aws.amazon.com/general/latest/gr/sigv4-signed-request-examples.html#sig-v4-examples-post . But for some reasons, the implementation in the boto3 library doesn't do that. Using the boto3 library is much easier than the code above, as I don't need to provide the credentials for the function.
You send an empty post request. You should add the data to the request:
import requests
r = requests.post(response, data = params)
I think you are having this issue, that's why you are recieving a 404.
They recommend using Cognito for authentication instead of IAM for this cases.
I am currently trying to understand Paypal APIs by writing a flask app which consumes various APIs. What i have bugun with is interacting with Permission services for classic APIs. I have a sandbox account which provides me with the necessities to make my requests. In one of my view functions i have succefully accessed the request permission page and received a the paypal's redirect to the callback Url i provided in my request. This is my code(note that i am still 'young' in python so suggestions for improvement are welcomed)
#account.route('/grant_auth')
def get_request_token():
"""handle the proccess of getting the request token"""
if request.method == 'GET':
url = "https://svcs.sandbox.paypal.com/Permissions/RequestPermissions"
headers = {
'X-PAYPAL-SECURITY-USERID': '**********',
'X-PAYPAL-SECURITY-PASSWORD': '*********',
'X-PAYPAL-SECURITY-SIGNATURE': '************',
'X-PAYPAL-REQUEST-DATA-FORMAT': 'JSON',
'X-PAYPAL-RESPONSE-DATA-FORMAT': 'JSON',
'X-PAYPAL-APPLICATION-ID': 'APP-80W284485P519543T'
}
payload = {
'scope': 'ACCOUNT_BALANCE',
'callback': 'http://127.0.0.1:5000/access_token',
'request_Envelop': {
'errorLanguage': 'en_US'
}
}
res_details = requests.post(
url, data=json.dumps(payload), headers=headers)
res_json_format = res_details.json()
if str(res_json_format['responseEnvelope']['ack']) == 'Failure':
return render_template('account.html', response='something went wrong with the application')
if str(res_json_format['responseEnvelope']['ack']) == 'Success':
token = res_json_format['token']
pal_permission_url = 'https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_grant-permission&request_token={}'.format(token)
return redirect(pal_permission_url)
else:
return render_template('account.html', response='something went wrong with the application')
Thats what i have done:
When i run that code and i visit /grant_auth
I am redirected to the paypal page where i can grant the permission when i grant i am redirected to http://127.0.0.1:5000/access_token?request_token=somevalue&verification_code=somevalue with an additonal parameters appended to it as such.
Please help me how i can obtain the verifiaction_code and request_token from the url(or even better what is the best direction to go from here) since i need the two values to make another request to the GetAccesToken API so that i can make requests on behalf of users who have granted the permission.
Retrieving parameters from a URL
seems to slightly address my problem but not fully because the url being parsed there is already existing unlike my case where the url changes. Thanks in Advance
I am trying to create an admin command that will simulate some api calls associated with a view but I don't want to hard code the url, for example like that url='http://127.0.0.1:8000/api/viewname', in order to send the request.
If I use the reverse option I can obtain half the url /api/viewname.
If I try to post the request that way
url = reverse('name-of-view')
requests.post(url, data=some_data)
I get
requests.exceptions.MissingSchema: Invalid URL '/api/viewname/': No schema supplied. Perhaps you meant http:///api/viewname/?
Do I have to look whether the server is running on the localhost or is there a more generic way?
requests module needs the absolute url to post to. you need
url = 'http://%s%s' % (request.META['HTTP_HOST'], reverse('name-of-view'))
requests.post(url, data=some_data)