Get context data from Lambda Authorizer (APi Gateway) - python

I'm using the Aws Lambda authorizer to secure an Api gateway. The authorizer lambda function is written in python using this blueprint from aws (https://github.com/awslabs/aws-apigateway-lambda-authorizer-blueprints/blob/master/blueprints/python/api-gateway-authorizer-python.py)
I added this code in the "blueprint"
if(event['authorizationToken'] == 'allow'):
policy.allowAllMethods()
else:
policy.denyAllMethods()
# Finally, build the policy
authResponse = policy.build()
# new! -- add additional key-value pairs associated with the authenticated principal
# these are made available by APIGW like so: $context.authorizer.<key>
# additional context is cached
context = {
'key': 'somevalue, # $context.authorizer.key -> value
'number' : 1,
'bool' : True
}
# context['arr'] = ['foo'] <- this is invalid, APIGW will not accept it
# context['obj'] = {'foo':'bar'} <- also invalid
authResponse['context'] = context
return authResponse
However in the lambda function attached to the route i cannot find the context value from authorizer. How can i get the values from context[key] ?

The solution is to use Mapping Templates on Integration Request. If you look at the route pipeline you will see that before reaching the Lambda Function you have a "Integration Request" section (and also a Integration Response)
In Integration Request you have the option to edit the input into lambda function via Mapping Templates.
So, i created a new Mapping Template ( use "Where" there are no Templates defined)
Content -Type use application/json
and in the actual template use something like
#set($inputRoot = $input.path('$'))
{
"key":"$context.authorizer.key"
}
Attention : the above template will remove the original output. That data is found in $inputRoot and you can add it to response using this format
{
"key":"$context.authorizer.key",
"originalkey":$inputRoot.originalkey
}

With the help of accepted answer I had came up with this:
## See http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html
## This template will pass through all parameters including path, querystring, header, stage variables, and context through to the integration endpoint via the body/payload
#set($inputRoot = $input.path('$'))
#set($authorizer = $context.authorizer)
#set($allParams = $input.params())
{
#foreach($key in $inputRoot.keySet())
"$key" : "$util.escapeJavaScript($inputRoot.get($key))"
#if($foreach.hasNext),#end
#end,
"context" : {
"params" : {
#foreach($type in $allParams.keySet())
#set($params = $allParams.get($type))
"$type" : {
#foreach($paramName in $params.keySet())
"$paramName" : "$util.escapeJavaScript($params.get($paramName))"
#if($foreach.hasNext),#end
#end
}
#if($foreach.hasNext),#end
#end
},
"stage-variables" : {
#foreach($key in $stageVariables.keySet())
"$key" : "$util.escapeJavaScript($stageVariables.get($key))"
#if($foreach.hasNext),#end
#end
},
#foreach($key in $context.keySet())
"$key" : "$util.escapeJavaScript($context.get($key))"
#if($foreach.hasNext),#end
#end,
"authorizer": {
#foreach($key in $authorizer.keySet())
"$key" : "$util.escapeJavaScript($authorizer.get($key))"
#if($foreach.hasNext),#end
#end
}
}
}
edit:
after tweaking around in api gateway I'we found out about
Use Lambda Proxy integration toggle button that adds extra parameters to event object

Related

Swift HTTP session not sending actual Request

So I have some Swift code that send a request to my local host
//
// ContentView.swift
// Shared
//
// Created by Ulto4 on 10/23/21.
//
import SwiftUI
struct ContentView: View {
var body: some View {
VStack{
Text("Hello, world!")
.padding()
Button(action : {
self.fu()
}, label: {
Image(systemName: "pencil").resizable().aspectRatio(contentMode:.fit)
})
}
}
func fu(){
let url = URL(string: "http://127.0.0.1:5000/232")
guard let requestUrl = url else { fatalError() }
var request = URLRequest(url: requestUrl)
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
if let error = error {
print("Error took place \(error)")
return
}
if let response = response as? HTTPURLResponse {
print("Response HTTP Status code: \(response.statusCode)")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
}
However, on my Flask app there are no get requests coming in and the function isn't running. There also isn't anything printing to the console.
I am fairly new to swift so I don't really know how to fix this.
Is there any other way to send requests in swift, if not, How would I fix this?
You are creating the URLSessionDataTask, but you never start it. Call task.resume(), e.g.
func performRequest() {
guard let url = URL(string: "http://127.0.0.1:5000/232") else {
fatalError()
}
let task = URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
print("Error took place \(error)")
return
}
if let response = response as? HTTPURLResponse {
print("Response HTTP Status code: \(response.statusCode)")
}
}
task.resume() // you must call this to start the task
}
That having been said, a few caveats:
You are doing http rather than https. Make sure to temporarily enable insecure network requests with app transport settings, e.g.
You didn’t say if this was for macOS or iOS.
If running on physical iOS device, it will not find your macOS web server at 127.0.0.1 (i.e., it will not find a web server running on your iPhone). You will want to specify the IP number for your web server on your LAN.
If macOS, make sure to enable outbound network requests in the target’s “capabilities”:
You asked:
Is there any other way to send requests in swift?
It is probably beyond the scope of your question, but longer term, when using SwiftUI, you might consider using Combine, e.g., dataTaskPublisher. When running a simple “what was the status code” routine, the difference is immaterial, but when you get into more complicated scenarios where you have to parse and process the responses, Combine is more consistent with SwiftUI’s declarative patterns.
Let us consider a more complicated example where you need to parse JSON responses. For illustrative purposes, below I am testing with httpbin.org, which echos whatever parameters you send. And I illustrate the use of dataTaskPublisher and how it can be used with functional chaining patterns to get out of the mess of hairy imperative code:
struct SampleObject: Decodable {
let value: String
}
struct HttpBinResponse<T: Decodable>: Decodable {
let args: T
}
class RequestService: ObservableObject {
var request: AnyCancellable?
let decoder = JSONDecoder()
#Published var status: String = "Not started yet"
func startRequest() {
request = createRequest().sink { completion in
print("completed")
} receiveValue: { [weak self] object in
self?.status = "Received " + object.value
}
}
func createRequest() -> AnyPublisher<SampleObject, Error>{
var components = URLComponents(string: "https://httpbin.org/get")
components?.queryItems = [URLQueryItem(name: "value", value: "foo")]
guard let url = components?.url else {
fatalError("Unable to build URL")
}
return URLSession.shared.dataTaskPublisher(for: url)
.map(\.data)
.decode(type: HttpBinResponse<SampleObject>.self, decoder: decoder)
.map(\.args)
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}
}
struct ContentView: View {
#ObservedObject var requestService = RequestService()
var body: some View {
VStack{
Text("Hello, world!")
.padding()
Button {
requestService.startRequest()
} label: {
Image(systemName: "pencil").resizable().aspectRatio(contentMode:.fit)
}
Text(requestService.status)
}
}
}
But, like I said, it is beyond the scope of this question. You might want to make sure you get comfortable with SwiftUI and basic URLSession programming patterns (e.g., making sure you resume any tasks you create). Once you have that mastered, you can come back to Combine to write elegant networking code.
FWIW, like workingdog said, you could also use the new async-await rendition of data(for:delegate:). But when in the declarative world of SwiftUI, I would suggest Combine.

Saving Longitude and Latitude to a user profile in Django

I was looking for a way to use HTML5 (and possibly JS) to save visitor/user Longitudnal & Latitudnal data to a database. I do not wish to use packages out there as they seem a bit outdated and may break my code in future considerting their own reliance on other APIs.
I know there is a way around using AJAX, but I clearly dont know and understand it well enough to implement it.
My ask of the learned Lords is - 1. Get Loc data 2. Send it to Python in dict or json or string format from where it can be further processed and saved.
Why you may ask - Good question. I would use it for displaying weather on the homepage and local twitter trends on a 'logged-in' page.
Any assistance would be appreciated.
Cheers!
My JS code is below:
// Set up global variable
var result;
function showPosition() {
// Store the element where the page displays the result
result = document.getElementById("result");
// If geolocation is available, try to get the visitor's position
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(successCallback, errorCallback);
result.innerHTML = "Getting the position information...";
} else {
alert("Sorry, your browser does not support HTML5 geolocation.");
}
};
// Define callback function for successful attempt
function successCallback(position) {
result.innerHTML = [position.coords.latitude, position.coords.longitude];
}
// Define callback function for failed attempt
function errorCallback(error) {
if (error.code == 1) {
result.innerHTML = "You've decided not to share your position, but it's OK. We won't ask you again.";
} else if (error.code == 2) {
result.innerHTML = "The network is down or the positioning service can't be reached.";
} else if (error.code == 3) {
result.innerHTML = "The attempt timed out before it could get the location data.";
} else {
result.innerHTML = "Geolocation failed due to unknown error.";
}
}
window.onload = showPosition;
what works for me:
First I recommend you to use models.PointField on your model.
When I obtain the long/lat data on FE, I send it as form-data in the following format eg:
"{\"type\":\"Point\",\"coordinates\":[14.215641,50.0100000001]}"
Then I map it to the model field and save it. It saves well and later I am able to query google geocoder or anything with it.
In your JS code:
function successCallback(position) {
result.innerHTML = [position.coords.latitude, position.coords.longitude];
$.post('your-python-endpoint-url', {
latitude: position.coords.latitude,
longitude: position.coords.longitude
});
}
In your python:
def index(request):
if request.is_ajax():
if request.method == 'POST':
print 'Raw Data: "%s"' % request.body
return HttpResponse("OK")
Change the method name and body according to your needs and don't forget to define a route in django.

Creating rest api with get method using AWS amplify, with a python lambda function

Is there a way to set up a rest api using a python lambda function with a get method that uses query parameters by using the amplify CLI or editing the code?
I know this can be done through the AWS Management Console, but was hoping for a more code-oriented solution. Below is the sample lambda I'm trying to use and a simple example of how I would like to get different api responses (length of dummy text string) based on the get method called by the api using something like "curl https://......../myapi?length=4"
import json
def handler(event, context):
print('received event:')
str_len = event['queryStringParameters']['length']
body = {
"message" : "DUMMY TEST"[1:str_len]
}
response = {
"statusCode" : 200,
"body" : json.dumps(body),
"headers" : {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*"
}
}
return response

How can I pass input (event) parameters through API Gateway into Lambda

I understand that API Gateway (AG) passes params into Lambda as JSON payload.
I have setup a POST API with Lambda Proxy in AG. My lambda function takes the following param list :
{
"fullname": "Mr xxxxx",
"clientemail": "xxxxxx#xxx.com",
"clientphone": "0800 088 8888",
"locationtext": "Laxxxxxx Hotel , CA, USA",
"subject": "Gxxxxxrth",
"appointmentblock_min": "60",
"buffer": "120",
"calendar_id": "xxxxx",
"thedate": "2021-03-2xxxxxx",
"thetime": "xxxxx"
}
Testing directly within the Lambda console. Everything works fine. I map all parameters in the code using the lambda EVENT object as follows :
fullname = event['fullname']
clientemail = event['clientemail']
appointmentblock_min = int(event['appointmentblock_min'])
... and so on.
All code works fine.
Adding the API Gateway component on top ... and things don't work.
The specific problem : how can I map the lambda input parameters coming from API Gateway (AG) to lambda ?
I realise that AG is sending JSON into Lambda. How to parse this payload to extract and use the parameters that I need.
I have tried to create a dict containing all the parameters :
Event = {}
Event = json.loads(body)
Event = json.loads(event.body)
No docs explicitly explain how to grab the event object params via AG.
Any help would be appreciated.
PS I am expecting to return data back to AG as follows :
return {
'statusCode': 200,
'headers': {'Content-Type': 'application/json'},
'body': json.dumps(out_message),
'isBase64Encoded': 'false'
}
Thank you for your consideration.
According to TestEvents in Lambda, the input you get on Lambda will be
LambdaTest
So you can load your payload (or body) as
Event = json.loads(event['body'])

Updating Zapier Storage programmatically

I have been working with the Zapier storage api through the store.zapier.com endpoint and have been successful at setting and retrieving values. However I have recently found a need to store more complex information that I would like to update over time.
The data I am storing at the moment looks like the following:
{
"task_id_1": {"google_id": "google_id_1", "due_on": "2018-10-24T17:00:00.000Z"},
"task_id_2": {"google_id": "google_id_2", "due_on": "2018-10-23T20:00:00.000Z"},
"task_id_3": {"google_id": "google_id_3", "due_on": "2018-10-25T21:00:00.000Z"},
}
What I would like to do is update the "due_on" child value of any arbitrary task_id_n without having to delete and add it again. Reading the API information at store.zapier.com I see you can send a patch request combined with a specific action to have better control over the stored data. I attempt to use the patch request and the "set_child_value" action as follows:
def update_child(self, parent_key, child_key, child_value):
header = self.generate_header()
data = {
"action" : "set_child_value",
"data" : {
"key" : parent_key,
"value" : {child_key : child_value}
}
}
result = requests.patch(self.URL, headers=header, json=data)
return result
When I send this request Zapier responds with a 200 status code but the storage is not updated. Any ideas what I might be missing?
Zapier Store doesn't seem to be validating the request body past the "action" and "data" fields.
When you make a request with the "data" field set to an array, you trigger a validation error that describes the schema for the data field (What a way to find documentation for an API! smh).
In the request body, the data field schema for "set_child_value" action is:
{
"action" : {
"enum": [
"delete",
"increment_by",
"set_child_value",
"list_pop",
"set_value_if",
"remove_child_value",
"list_push"
]
},
"data" : {
"key" : {
"type": "object"
},
"values" : {
"type": "object"
}
}
}
Note that it's "values" and not "value"
I was able to update specific child values by modifying my request from a PATCH to a PUT. I had to do away with the data structure of:
data = {
"action" : "set_child_value",
"data" : {
"key" : parent_key,
"value" : {child_key : child_value}
}
and instead send it along as:
data = {
parent_key : {child_key : child_value}
}
My updated request looks like:
def update_child(self, parent_key, child_key, child_value):
header = self.generate_header()
data = {
parent_key : {child_key : child_value}
}
result = requests.put(self.URL, headers=header, json=data)
return result
Never really resolved the issue with the patch method I was attempting before, it does work for other Zapier storage methods such as "pop_from_list" and "push_to_list". Anyhow this is a suitable solution for anyone who runs into the same problem.

Categories

Resources