How to make a Flask Endpoint? - python

As you will be able to see from my previous questions, I have been working on a project, and really want to know how I can get this last part finished.
Quick summary of project: I have a Raspberry Pi that is running a Web Server (Lighttpd) and Flask. It has an RF USB Transmitter connected, which controls the power of a plug via a Python script. (Power.pyon GitHub). This works.
I now need to create an Endpoint in Flask so that Salesforce can send it some JSON, and it will understand it.
I want to keep this as simple as I can, so I understand what it's actually doing. In my last question, someone did provide me with something, but I thought it'd be better have a specific question relating to it, rather than trying to cover too much in one.
All I need to be able to send is 'power=on/off', 'device=0,1,2', 'time=(secondsasinteger)' and 'pass=thepassword' I can send this as URL variables, or a POST to my existing power.py linked above, and it does it.
I would like a simple, clear way of sending this from Salesforce in JSON, to Flask and make it understand the request.
Literally all I need to do now is go to: ip/cgi-bin/power.py?device=0&power=on&time=10&pass=password
That would load a Python script, and turn device 0 on for 10 seconds. (0 is unlimited).
How can I convert that to JSON? What code do I need to put into Flask for it to be able to comprehend that? Can I forward the variables onto the power.py so the Flask file only has to find the variables and values?
I have downloaded Postman in Chrome, and this allows me to send POST's to the Pi to test things.
Where can I find out more info about this, as a beginner?
Can I send something like this?
'requestNumber = JSONRequest.post(
"ip/api.py",
{
deviceid: 0,
pass: "password",
time: 60,
power: "on"
},'

I don't know how you can get saleforce to send a POST request with an associated JSON, but capturing it with Flask is fairly easy. Consider the following example:
from flask import request
from yourmodule import whatever_function_you_want_to_launch
from your_app import app
#app.route('/power/', methods=['POST'])
def power():
if request.headers['Content-Type'] == 'application/json':
return whatever_function_you_want_to_launch(request.json)
else:
return response("json record not found in request", 415)
when saleforce visits the url http://example.com/power/ your applications executes the power() function passing it, as a parameter, a dictionary containing the JSON contents. The whatever_function_you_want_to_launch function can use the dictionary to trigger whatever action you want to take, and return a response back to the power() function. The power() function would return this respose back to salesforce.
For example:
def whatever_function_you_want_to_launch(data):
device = data['deviceid']
power = data['power']
message = ""
if power == "on":
turn_power_on(device)
message = "power turned on for device " + device
else:
turn_power_off(device)
message = "power turned off for device " + device
return make_response(message, 200)
this is just a short example, of course. You'd need to add some additional stuff (e.g. handle the case that the JSON is malformed, or does not contain one of the requested keys).
in order to test the whole stuff you can also use curl command (available on Linux, don't know on other OSs) with this type of syntax:
curl -H "Content-type: application/json" -X POST http://localhost:5000/power/ -d '{"deviceid": "0", "pass": "password", "time": "60", "power": "on"}'

Related

Is it possible to inject python code in Kwargs and how could I prevent this user input

I'm at the moment in the middle of writing my Bachelor thesis and for it creating a database system with Postgres and Flask.
To ensure the safety of my data, I was working on a file to prevent SQL injections, since a user should be able to submit a string via Http request. Since most of my functions which I use to analyze the Http request use Kwargs and a dict based on JSON in the request I was wondering if it is possible to inject python code into those kwargs.
And If so If there are ways to prevent that.
To make it easier to understand what I mean, here are some example requests and code:
def calc_sum(a, b):
c = a + b
return c
#app.route(/<target:string>/<value:string>)
def handle_request(target,value):
if target == 'calc_sum':
cmd = json.loads(value)
calc_sum(**cmd)
example Request:
Normal : localhost:5000/calc_sum/{"a":1, "b":2}
Injected : localhost:5000/calc_sum/{"a":1, "b:2 ): print("ham") def new_sum(a=1, b=2):return a+b":2 }
Since I'm not near my work, where all my code is I'm unable to test it out. And to be honest that my code example would work. But I hope this can convey what I meant.
I hope you can help me, or at least nudge me in the right direction. I've searched for it, but all I can find are tutorials on "who to use kwargs".
Best regards.
Yes you, but not in URL, try to use arguments like these localhost:5000/calc_sum?func=a+b&a=1&b=2
and to get these arguments you need to do this in flask
#app.route(/<target:string>)
def handle_request(target):
if target == 'calc_sum':
func= request.args.get('func')
a = request.args.get('a')
b = request.args.get('b')
result = exec(func)
exec is used to execute python code in strings

Modifying HTTPS response packet on the fly with mitmproxy

I am trying to implement an mitmproxy addon script, in order to tamper with a particular https packet data - which is by the way decrypted on the fly through mitmproxy's certificate injection.
I am following this Stack Overflow answer to a rather similar question, as well as this tutorial from the mitmproxy docs, but without any success so far.
The packet I'm targeting comes from https://api.example.com/api/v1/user/info.
Now here is the whole python script I wrote so as to tamper with this packet data, based upon the aforementioned sources :
from mitmproxy import ctx
class RespModif:
def _init_(self):
self.num = 0
def response(self, flow):
ctx.log.info("RespModif triggered")
if flow.request.url == "https://api.example.com/api/v1/user/info":
ctx.log.info("RespModif triggered -- In the if statement")
self.num = self.num + 1
ctx.log.info("RespModif -- Proceeded to %d response modifications "
"of the targetted URLs" % self.num)
addons = [
RespModif()
]
Checking out the events log, I'm able to see that the first log information ("RespModif triggered") is being reported onto the log, but the two other log infos (done from inside the if statement) are never reported, which means I think that the if statement does never succeed.
Is there something wrong with my code ?
How can I get the if statement to succeed ?
PS: The target URL is definitely correct, plus I'm using it with a registered account from the client application that is being sniffed with mitmproxy.
Have you tried to use pretty_url attribute ?
Something like :
if flow.request.pretty_url == "https://api.example.com/api/v1/user/info":
....
pretty_url attribute handles full domain name whereas url only deals with corresponding ip address.
Also logging the content of pretty_url should allow to see what exact URL is going through and give more visibility as to what the code is actually doing.

Bad request type PUT when sending PUT request through Qt, works with curl

I have subclassed BaseHTTPRequestHandler and implemented do_GET(), do_POST() and do_PUT().
I thought that everything works fine until I put my Qt application to the test. The GET request works and the server sends an XML document with some data that the application processes. The POST request also works and is used to generate test cases through REST.
As for the PUT things are looking weird.
Here is the PUT handler on the server side:
def do_PUT(self):
"""
Processes PUT request. It can be tested using wget, curl or any
other tool with support for http PUT. Use this to send reports
about the current status of device and all of its slave devices
Example:
curl -X PUT -d "status=downloading" http://127.0.0.1:8090/so/updateProgress
"""
print('Processing PUT request...')
params_parsed = urlparse(self.path)
params = parse_qs(params_parsed.query)
if len(params) > 0 or '/so/updateProgress' not in params_parsed.path:
print('Use "/so/updateProgress" to report on update progress')
self.__set_headers(data_type='text/html')
self.send_error(501)
return
self.__set_headers(data_type='text/xml')
report_raw = self.rfile.read(int(self.headers.getheader('Content-length')))
print('Received report')
print('Parsing report...')
# Generate XML from the raw data and validate it
report = SoRequestHandler.Report.from_xml(report_raw)
print('Parsing of report complete')
report.write_to_file(log_path='report_log', append=True)
self.send_response(200)
In my Qt application I have a class called ReportPublisher, which takes some reports from all the devices it is connected to (through MQTT), aggregates the reports into a single one and sends it to the server, which logs it in a file:
void ReportPublisher::publishHttpReport(HttpReport report)
{
QString reportXml = report.xml().toString();
if (reportXml.isEmpty())
{
LOG(ERROR) << "Something went wrong with the generation of HTTP report";
return;
}
LOG(INFO) << "Generated report for SO server: " << reportXml;
QByteArray reportRaw = reportXml.toUtf8();
QUrl target(this->urlServer);
target.setPath(this->urlPath);
QNetworkRequest req(target);
req.setHeader(QNetworkRequest::ContentTypeHeader, QString("application/xml"));
this->reply = this->nam->put(req, reportRaw);
// TODO Read back response (expected code 200)?
}
I have to be honest. I have never done a PUT request in Qt and all I've done so far were GET requests so there might be some really fundamental yet easy to spot error in the code above.
The data that the server receives looks like this:
<updateProgress xmlns='service.so.de/so'>
<deviceTypeId>...</deviceTypeId>
<packageId>...</packageId>
<version>...</version>
<status>...</status>
</updateProgress>
If I use curl like this
root#obunit:~$ curl -X PUT -i 'http://192.168.120.61:8090/so/updateProgress' -d "<updateProgress xmlns='service.so.de/so'><deviceTypeId>...</deviceTypeId><packageId>...</packageId><version>...</version><status>...</status></updateProgress>"
where 192.168.120.61:8090 is the IP address and the port the server is located at/listening to for incoming requests I have no problem.
However with data coming from my Qt application I get
192.168.120.172 - - [11/Apr/2018 15:32:37] code 400, message Bad HTTP/0.9 request type ('PUT')
192.168.120.172 - - [11/Apr/2018 15:32:37] "PUT HTTP/1.1" 400 -
in my log (with 192.168.120.172 being the IP address of the system where my software is running.
From my scarce knowledge code 400 means invalid syntax, which can be due to following 2 reasons (at least what I can think of right now):
malformed data (invalid XML, JSON etc.)
incorrect encoding of the data, which is basically equivalent to the malformed data but coming from a different source.
I have tried converting the XML document that I'm generating to QByteArray using QDomDocument::toByteArray(int i). I have also tried (as you can see in the code) to convert the document to QString and then to a UTF-8 QByteArray but I can't get my server to process the data.
What's even stranger is that (as you can see in my do_PUT()) implementation my PUT handler starts with printing a message, which never appears in the logs hence the issue arises from somewhere deeper in the code of BaseHTTPRequestHandler.
The problem was rather simple yet very annoying.
Following the advice of #MrEricSir I did use Wireshark just to find out that get some weird TCP retramsission. Retransmission usually occurs due to network congestion, which in my case was not a possibility however it did indicate a network issue that has nothing to do with the XML content I was sending.
So I started my remote debugging session again and look closer to where I was emitting the PUT request. And there it was! There was a / missing between the base URL (IP:PORT) and the path. So instead of
PUT 192.168.120.61:8090/so/updateProgress
I was doing
PUT 192.168.120.61:8090so/updateProgress
which ultimately led to the PUT request not succeeding at all (hence the code 400).
Just a hint for anyone reading this in the future - whenever you manually set that path using QUrl::setPath(QString)
QUrl target(this->urlServer);
target.setPath(this->urlPath);
ALWAYS make sure that the string you pass as a path starts with / because Qt isn't going to add one!

Using IFTTT to check JSON content and send Email or Text Message

I am trying to learn how to use the new WebHooks service on IFTTT, and I'm struggling to figure out how it is supposed to work. Most of the projects I can find online seem to refer to a deprecated "maker" service, and there are very little resources for interpreting the new channel.
Let's say I want to check the value of "online" every ten minutes in the following json file: https://lichess.org/api/users/status?ids=thibault
I can write a Python script that extracts this value like so:
response = urlopen('https://lichess.org/api/users/status?ids=thibault')
thibault = response.read()
data = json.loads(thibault)
status = data[0]['online']
If the status is equal to "true", I would like to receive a notification via email or text message. How do I integrate the python script and the webhooks service? Or do I even need to use this script at all? I assume I need some kind of cron job that regularly runs this Python script, but how do I connect this job with IFTTT?
When I create a new applet on IFTTT I am given the ability to create a trigger with a random event name, but it's unclear what that event name corresponds to.
I have a similar setup for my IFTTT webhook service. To the best of my understanding, the answer to your question is yes, you need this script (or similar) to scrap the online value, and you'll probably want to do a cron job (my approach) or keep the script running (wouldn't be my preference).
IFTTT's webhooks a json of up to 3 values, which you can POST to a given event and key name.
Below is a very simple excerpt of my webhook API:
def push_notification(*values, **kwargs):
# config is in json format
config = get_config()
report = {}
IFTTT = {}
# set default event/key if kwargs are not present
for i in ['event', 'key']:
IFTTT[i] = kwargs[i] if kwargs and i in kwargs.keys() else config['IFTTT'][i]
# unpack values received (up to 3 is accepted by IFTTT)
for i, value in enumerate(values, 1):
report[f"value{i}"] = value
if report:
res = requests.post(f"https://maker.ifttt.com/trigger/{IFTTT['event']}/with/key/{IFTTT['key']}", data=report)
# TODO: add try/except for status
res.raise_for_status()
return res
else:
return None
You probably don't need all these, but my goal was to set up a versatile solution. At the end of the day, all you really need is this line here:
requests.post(f"https://maker.ifttt.com/trigger/{event}/with/key/{key}", data={my_json_up_to_3_values})
Where you will be placing your webhook event name and secret key value. I stored them in a config file. The secret key will be available once you sign up on IFTTT for the webhook service (go to your IFTTT webhook applet setting). You can find your key with a quick help link like this: https://maker.ifttt.com/use/{your_secret_key}. The event can be a default value you set on your applet, or user can choose their event name if you allow.
In your case, you could do something like:
if status:
push_notification("Status is True", "From id thibault", event="PushStatus", key="MysEcR5tK3y")
Note: I used f-strings with version 3.6+ (It's great!), but if you have a lower version, you should switch all the f-strings to str.format().

Create own IVR that will access Database

I'm currently interning with a company and have been tasked with researching some methods of using telephony. The goal is to provide our clients with the ability to call in and through an IVR-prompted questions, get information back. The information will be from our database.
I have successfully done this using Twilio and a small python app. It does exactly what I'm looking to do, except the cost factor can be a bit high, especially if we have 30,000+ clients calling for minutes on end.
My goal is to find a way to replicate what I've done with Twilio, but on our own server. I've found options like Asterisk and IncrediblePBX, but because my limited knowledge of Linux, every error I run into results in scouring the internet for answers. Ultimately, I'm not sure if I'm heading in the right direction.
This is an example of what I'd like to accomplish:
Client calls into number. They're directed to provide an account number, (possibly their phone number) At that point it will take this information and talk to a database. Gathering this information it will relay back to the client the status of their account etc.
Questions:
I was hoping to use Google Voice to route calls similar to Twilio, is this possible? Alternatively, could my company switch to a VoIP and do the same thing?
If I move away from Twilio, can Asterisk perform the necessary tasks? Receiving calls and running the app to gather database information.
Current code for Twilio, in Python:
from flask import Flask, request, redirect
import twilio.twiml
import json
from urllib.request import urlopen
app = Flask(__name__)
callers = {
"+": "Nicholas",
}
#app.route("/", methods=['GET', 'POST'])
def initial():
# Get the caller's phone number from the incoming Twilio request
from_number = request.values.get('From', None)
resp = twilio.twiml.Response()
# if the caller is someone we know:
if from_number in callers:
# Greet the caller by name
caller = callers[from_number]
else:
caller = ""
resp = twilio.twiml.Response()
resp.say("Hello " + caller)
resp.say("Thank you for calling.")
your_number = list(from_number)
del your_number[0]
del your_number[0]
resp.say("You are calling from: ")
x = 0
while x < len(your_number):
resp.say(your_number[x])
x += 1
print("Please enter the neighborhood I.D. you're looking for.")
with resp.gather(numDigits=1, action="/handle-first", method="POST") as g:
g.say("Please enter the neighborhood I.D. you're looking for.")
return str(resp)
#app.route("/handle-first", methods=['GET', 'POST'])
def handle_key():
digit_pressed = request.values.get('Digits', '')
resp = twilio.twiml.Response()
url = 'http://localhost/...'
response = urlopen(url)
data = json.loads(response.readall().decode('utf-8'))
current = data['rows'][0]['Neighborhood']
print(current)
resp.say("You have chosen " + current + "as your neighborhood.")
with resp.gather(numDigits=1, action="/handle-second", method="POST") as h:
h.say("Press 1 to choose another Neighborhood?")
return str(resp)
#app.route("/handle-second", methods=['GET', 'POST'])
def handle_key2():
digit_pressed = request.values.get('Digits', '')
resp = twilio.twiml.Response()
if digit_pressed == "1":
return redirect("/")
else:
resp.say("Thank you for calling. Good-bye.")
return str(resp)
if __name__ == "__main__":
app.run(debug=True)
Asterisk is fundamentally different than Twilio, I can honestly say they are somewhat opposites. The track you are seeking to follow is that of Asterisk dialplan with a combination of AGI. AGI is the Asterisk Gateway Interface, and will enable you a way to interact with Asterisk via a simple scripting mechanism. The API is fairly simple and there is a whole range of ready made libraries you can use. Let's put it this way, when it comes to AGI, just pick your poison - PHP, Python, JAVA, Ruby - all are available.
My personal favorite, and many in the Asterisk world as well, is PHP - with an LGPL component called PHP-AGI. It's fairly old and has been around for years, but works well with all versions of Asterisk. If you wish to walk on the bleeding edge and require stronger call control, you can go with ARI (Asterisk REST interface), however, judging from your requirement - that's an overkill.
You can read a bit about AGI development with PHP at this link:
https://www.packtpub.com/books/content/asterisk-gateway-interface-scripting-php
(Shameful plug - I wrote the book)
In addition, you can refer to the following presentation for additional information (again, my own presentation) - It's a little old, but still valid in terms of concept and usage:
http://osdc.org.il/2006/html/Asterisk_AGI_Programming.ppt
Good luck
Yes, asterisk can do all task you can program. It even have api for access raw stream.
However no, you can't use SAME code.
For google voice check chan_motif code.
For db access use dialplan+realtime or func_odbc or fastagi/agi interface.
You are probably looking for an open source IVR solution. The only one I am aware of is OpenVBX.
I am in no way affiliate with them or Twilio.

Categories

Resources