Permission Issue at an AWS API using Lambda - amazon-web-services

I'm testing my newly deployed AWS API using https://www.apitester.com/.
As you can see i cant access the API. The API is deployed and the Lambda code looks as following.
const AWS = require('aws-sdk');
var bucket = new AWS.S3();
exports.handler = (event, context, callback) => {
let data =JSON.parse(event.body);
var params = {
"Body": data,
"Bucket": "smartmatressbucket",
// "Key": filePath
};
bucket.upload(params, function(err, data){
if(err) {
callback(err, null);
} else {
let response = {
"statusCode": 200,
"headers": {
"my_header": "my_value"
},
"body": JSON.stringify(data),
"isBase64Encoded": false
};
callback(null, response);
}
});
};

Looking at the response log, it seems the API Gateway generates "ForbiddenException". I believe the most possible reason is using an incorrect API URL (eg- https://ogk2hm09j0.execute-api.eu-central-1.amazonaws.com/).
Suppose you configure the Lambda function to a GET method of a resource name "resourceA". Then you deploy the API to a stage named "dev". Then the correct URL should be https://ogk2hm09j0.execute-api.eu-central-1.amazonaws.com/dev/resourceA
But looking at the API URL in the logs, it seems the stage name or the resource name is not specified.

Related

Getting GET or POST parameters via lambda functional

Recently amazon has released Function Url in Lambda.
I just want to know if we can access Query parameters using it , without using the gateway API.
For example https://functionalurl.aws?id=123
I need to get 123
exports.handler = async (event, context, callback) => {
console.log(event);
callback(null, {
statusCode: 200,
body: context,
headers: {
"Access-Control-Allow-Origin": "*",
},
});
};
Event is always null when I access the function url.

How to Validate a file which user send by Presigned URl to S3 Bucket during upload file

When user send a file or any data by Presigned URL to S3 Bucket. In between no restriction. So User Can send anything by Presigned URL to S3 Bucket.
But I want check data between Presigned URL and S3 Bucket which data user send.
I am using serverless framework.
please help me, Thanks in Advance.
My lambda function code here
module.exports.uploadLarge = async (event) => {
console.log({ event })
try {
const body = JSON.parse(event.body);
console.log({ body })
const action = body.action;
const type = body.type;
const key = body.key;
const params = {
Bucket: BucketName,
Key: key,
// ContentType: type,
Expires: 10000,
}
if (action === "putObject") {
params.ContentType = type;
// params.Expires = 20000
}
console.log({ params })
// const url = S3.getSignedUrlPromise(action, params);
const u = S3.getSignedUrl(action, params);
console.log({ u });
// console.log({ url });
return {
statusCode: 200,
body: JSON.stringify({ u }),
headers: {
// "Content-Type": "application/json"
'Access-Control-Allow-Origin': '*',
}
}
} catch (err) {
return {
statusCode: 500,
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*"
},
body: JSON.stringify(err)
}
}
}
But I want check data between Presigned URL and S3 Bucket which data user send.
Its not possible with your current design. You can only perform a check after the user have uploaded the file. For example, setup an S3 trigger for PutObject event which will trigger a lambda function to verify the file. Otherwise, you have to change your architecture, and put some proxy between users and S3. For example, Apigateway, or CloudFront, or custom application.

AWS Enabling CORS for my API triggering a Lambda

I managed to create an AWS Lambda that does two things: writes on a dynamo DB and sends an SMS to a mobile number. Then I call this Lambda through a API Gateway POST call, and it works great from the Test section on AWS console but it gives error both on Postman and my own website. I inserted a callback to handle CORS on Lambda and deployed + enabled CORS on my API via console and deployed it but still get errors:
Errors via postman call: {
"message": "Internal server error"
}
Errors via my website (jquery ajax POST call): Lambda calling failed: {"readyState":4,"responseText":"{"message": "Internal server error"}","responseJSON":{"message":"Internal server error"},"status":500,"statusText":"error"}
This is my lambda code
const AWS = require('aws-sdk');
const dynamodb = new AWS.DynamoDB();
const SNS = new AWS.SNS();
const tableName = "#####";
let params = {
PhoneNumber: 'mynumber####',
Message: 'Someone wrote!'
};
exports.handler = (event, context, callback) => {
dynamodb.putItem({
"TableName": tableName,
"Item" : {
"Id": {
N: event.Id
},
"Type": {
S: event.Type
}
}
}, function(err, data) {
if (err) {
console.log('Error putting item into dynamodb failed: '+err);
}
else {
console.log('Success in writing, now starting to send SMS');
return new Promise((resolve, reject) => {
SNS.publish(params, function(err, data) {
if(err) {
console.log("Error in sending sms alarm");
reject(err);
}
else {
console.log("SMS alarm sent!");
resolve(data);
}
})
})
}
});
callback(null, {
statusCode: 200,
headers: {
"Access-Control-Allow-Headers" : "Content-Type",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "OPTIONS,POST,GET"
},
body: JSON.stringify('Hello from Lambda!'),
});
};
What am I doing wrong? I don't think permissions on lambda are the problem here, since testing it on console works both on writing on dynamo both sending the sms to my cellphone.
If I click from here on API Endpoint I get the same error {"message": "Internal server error"}
SOLUTION: instead of making an AWS HTTP API, make a AWS REST API, that is much more complex and offers more personalization for CORS, letting you set them and headers.

Sending http request from aws lambda to google firebse funcitons

I have set up firebase functions to receive http requests and have verified that the same is working. Now im trying to send http request to firebase from aws lambda function. But there is no response either in aws lambda or in the firebase functions log. This is my aws lambda code:
const postData = JSON.stringify({
"queryresult" : {
"parameters": {
"on": "1",
"device": "1",
"off": ""
}
}
});
const options = {
hostname: 'https://<the firebase function endpoint>',
port: 443,
path: '',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(postData)
}
};
const req = https.request(options, postData)
.then((response) => {
console.log(response);
})
.catch((err) => {
console.log(err);
});
// Write data to request body
req.write(postData);
req.end();
}
The promise part here is suppose to execute the console logs but it is not getting executed. Is there something that i'm missing here. The host is the URL that we obtain when we deploy a function. Or is there some firebase or aws related plan problem. I'am using the spark plan in firebase. Thankyou.

How to get the name of the stage in an AWS Lambda function linked to API Gateway

I have the following Lambda function configured in AWS Lambda :
var AWS = require('aws-sdk');
var DOC = require('dynamodb-doc');
var dynamo = new DOC.DynamoDB();
exports.handler = function(event, context) {
var item = { id: 123,
foo: "bar"};
var cb = function(err, data) {
if(err) {
console.log(err);
context.fail('unable to update hit at this time' + err);
} else {
console.log(data);
context.done(null, data);
}
};
// This doesn't work. How do I get current stage ?
tableName = 'my_dynamo_table_' + stage;
dynamo.putItem({TableName:tableName, Item:item}, cb);
};
Everything works as expected (I insert an item in DynamoDB every time I call it).
I would like the dynamo table name to depend on the stage in which the lambda is deployed.
My table would be:
my_dynamo_table_staging for stage staging
my_dynamo_table_prod for stage prod
However, how do I get the name of the current stage inside the lambda ?
Edit: My Lambda is invoked by HTTP via an endpoint defined with API Gateway
If you have checked "Lambda Proxy Integration" in your Method Integration Request on API Gateway, you should receive the stage from API Gateway, as well as any stageVariable you have configured.
Here's an example of an event object from a Lambda function invoked by API Gateway configured with "Lambda Proxy Integration":
{
"resource": "/resourceName",
"path": "/resourceName",
"httpMethod": "POST",
"headers": {
"header1": "value1",
"header2": "value2"
},
"queryStringParameters": null,
"pathParameters": null,
"stageVariables": null,
"requestContext": {
"accountId": "123",
"resourceId": "abc",
"stage": "dev",
"requestId": "456",
"identity": {
"cognitoIdentityPoolId": null,
"accountId": null,
"cognitoIdentityId": null,
"caller": null,
"apiKey": null,
"sourceIp": "1.1.1.1",
"accessKey": null,
"cognitoAuthenticationType": null,
"cognitoAuthenticationProvider": null,
"userArn": null,
"userAgent": "agent",
"user": null
},
"resourcePath": "/resourceName",
"httpMethod": "POST",
"apiId": "abc123"
},
"body": "body here",
"isBase64Encoded": false
}
I managed it after much fiddling. Here is a walkthrough:
I assume that you have API Gateway and Lambda configured. If not, here's a good guide. You need part-1 and part-2. You can skip the end of part-2 by clicking the newly introduced button "Enable CORS" in API Gateway
Go to API Gateway.
Click here:
Click here:
Then expand Body Mapping Templates, enter application/json as content type, click the add button, then select mapping template, click edit
And paste the following content in "Mapping Template":
{
"body" : $input.json('$'),
"headers": {
#foreach($param in $input.params().header.keySet())
"$param": "$util.escapeJavaScript($input.params().header.get($param))" #if($foreach.hasNext),#end
#end
},
"stage" : "$context.stage"
}
Then click the button "Deploy API" (this is important for changes in API Gateway to take effect)
You can test by changing the Lambda function to this:
var AWS = require('aws-sdk');
var DOC = require('dynamodb-doc');
var dynamo = new DOC.DynamoDB();
exports.handler = function(event, context) {
var currentStage = event['stage'];
if (true || !currentStage) { // Used for debugging
context.fail('Cannot find currentStage.' + ' stage is:'+currentStage);
return;
}
// ...
}
Then call your endpoint. You should have a HTTP 200 response, with the following response body:
{"errorMessage":"Cannot find currentStage. stage is:development"}
Important note:
If you have a Body Mapping Template that is too simple, like this: {"stage" : "$context.stage"}, this will override the params in the request. That's why body and headers keys are present in the Body Mapping Template. If they are not, your Lambda has not access to it.
For those who use the serverless framework it's already implemented and they can access to event.stage without any additional configurations.
See this issue for more information.
You can get it from event variable. I logged my event object and got this.
{ ...
"resource": "/test"
"stageVariables": {
"Alias": "beta"
}
}