python flask isn't passing on variable - javascript

I'm trying to build a python tool that sends an email to a helpdesk. it does this using flask, javascript, jQueryUI and python. I have set up a dialogue box to capture information needed for a ticket. this information (as well as some information about the user's browser) is then saved as a variable called emailinfo which is passed through a fetch request to flask, and then to a function in python that sends an email. I know that the function I've written will send an email because I've tested it. I also know that flask is receiving the emailInfo variable and that it has information in it, because flask prints the variable it receives and the JS console also prints the information it receives. both of these are fine, but when it tries to send the email, it doesn't send anything.
JavaScript/jQueryUI:
emailInfo = JSON.stringify("User's Name: " + name.val() + " "
+ "User's E-Mail: " + + email.val() + " "
+ "Reported Issue: " + issue.val() + " "
+ "More Details: " + " "
+ start.val(), null, 2)
console.log(emailInfo)
//get IP/browser information
$.getJSON('http://ip-api.com/json', function(data) {
//put together email info
emailInfo = emailInfo + " " + JSON.stringify(data, null, 2)
emailInfo = JSON.stringify({emailInfo})
console.log(emailInfo)
});
//fetch helpdesk function
//note: this is functionally the same as another fetch request I have written that works perfectly fine
fetch(`${scriptRoot}/helpdesk`, {
method: 'POST',
body: JSON.stringify({ mailInfo: emailInfo }),
headers: {
'Content-Type': 'application/json'
},
})
}
return valid;
}
Flask
(all functions from other file have been imported)
#app.post("/helpdesk")
def sendtodesk():
mailInfo = request.get_json().get("mailInfo")
mailInfo = str(mailInfo)
#extra info - this prints only the first line of mailInfo and not the scraped IP. this doesn't really matter to me, though, because I think I can fix it
print(mailInfo)
SendEmail(mailInfo)
return("Sent! Thank you.")
Python Email Script
note: I have tested this separately and it works
def SendEmail(emailInfo):
port = 465 # For SSL
smtp_server = "smtp.gmail.com"
#sender and reciever address. here its the helpbot's email and the address of the testing environment helpdesk
senderemail = "(email sender goes here)"
password = "(password to email sender goes here)"
recieveremail = "(email reciever goes here)"
#message
message = emailInfo
context = ssl.create_default_context()
with smtplib.SMTP_SSL(smtp_server, port, context=context) as server:
server.login(senderemail, password)
server.sendmail(senderemail, recieveremail, message)
I've tried a lot of things to get this to work. the best success I've had has been in making it similar to another flask function I've made that works perfectly fine, which has allowed me to get as far as printing the variable. I can't tell whether the variable gets through to the function, however I know that the SendEmail function is called because the sender sends an email. This is the email that is sent, there is nothing in there, which makes me think that flask never passes the variable on properly.
thanks for any and all help you can give me. I've already done a lot for this project (it is also an NLTK chatbot that works perfectly fine, which is how I've done flask before - the code here is very similar to the code used there) and I've really hit a brick wall with this after doing everything in my programming power.

turns out it was due to the format of the emailinfo variable. the smtp and ssl method of sending mail is a string, but requires a certain format. having colons broke it. I need to have colons because that's the format that the IP API returns the information I need in. switching to another email package (I used yagmail) has worked fine :)

First improvment, use string formatting:
let email_info = `User's Name: ${name.val()}\n User's E-Mail: ${email.val()}\n Reported Issue: ${issue.val()}\n More Details: ${start.val()}\n`;
Second improvment, create a second entry in json and seperate the data,
also use fetch api just to get data, and combine this data together on python side.
{ mailInfo: emailInfo, ipapiInfo: <your json data from ip-api>}
then u can better debug wich or where date get lost, maybe this helps u a little bit

Related

"redirect_uri_mismatch" when sending authentication code to GoogleAPI

I am having trouble with the authentication process for the GoogleAPI. In the end I want to be able to read the users steps using the GoogleFit API and then store that value in a database. Currently I'm using restdb.io and executing javascript in codehooks.
The documentation from Google that I am following can be found here, clicking on the HTTP/REST option in the code examples. At the moment I am at step 5: I have gotten the users authentication code and stored it in the database. Now I have to POST the code along with some other parameters and get the access and refresh tokens.
If the POST is successful (from what I understand) I should get back a 200-OK message that the request was valid. Google will then POST a JSON body with the access and refresh token to the redirect_uri that I have specified in my GoogleAPI credentials page and the initial request. At redirect_uri I have to handle the request and save the two values.
The problem is that I receive a redirect_uri_mismatch - Bad Request message from Google as a response when executing the request. I get it at the log.debug("ERROR HERE: " + [...]); in the code below:
async function mainFunction(){
const authCode = THIS_IS_MY_AUTHENTICATION_CODE;
try {
var answer = await postRequestToGoogle(authCode);
//do stuff with response from Google
} catch (error) {
//do stuff
}
}
async function postRequestToGoogle(authCode){
//body for the request
const params = "code=" + authCode + "&" +
"client_id=THIS_IS_MY_CLIENT_ID" + "&" +
"client_secret=THIS_IS_MY_CLIENT_SECRET" + "&" +
"redirect_uri=THIS_IS_MY_REDIRECT_URI" + "&" +
"grant_type=authorization_code";
try{
const result = await fetch('https://oauth2.googleapis.com/token', {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: params})
.then(res => {
log.debug("ERROR HERE: " + JSON.stringify(res.json()));
return res.json();
})
//return JSON back main function
return result;
}catch(error){
//do stuff
}
}
I looked up the error message and tried some things:
Copy and pasted multiple different Authorized redirect URI from the GoogleAPI credentials page into the code to make sure that there is no problem with
http/https
www/no www
trailing slashes
typos or capitalization
Waited for changes to be processed by Google (read that it can more than 30min)
Changed all the other parameters to see if the redirect_uri is actually the problem
If code is changed the message is invalid_grant - Bad Request
If client_id is changed the message is invalid_client - The OAuth client was not found
If client_secret is changed the message is invalid_client - Unauthorized
If the grant_type is changed the message is unsupported_grant_type - Invalid grant_type
That's why I think the issue is the redirect_uri, but it's unclear to me how since I copy&pasted it. Something that came to mind was that maybe the value of redirect_uri gets changed when it's read by Google? Or maybe when the request is being put together? Do some characters have to be replaced?
I tried to analyze the request with Wireshark but didn't think about the fact that it's HTTPS so I would have I would have to decrypt it.. Is that something I should look into?
Thank you for taking the time to read all of this! If you have any advice please let me know :)
Update 16.11.20:
I have created a new OAuth 2.0 Client ID and used the new id/secret in my request. The resulting message the same as before. I will wait and try again tomorrow to see if maybe Google needs some more time. Then I'll try to delete all current IDs and start with a fresh GoogleAPI project.
Update 19.11.20:
Creating a new OAuth 2.0 Client ID did not resolve my problem, neither did creating a whole new GoogleAPI project and adding those credentials into the request. I am in contact with the developers of restdb.io and have asked them to add the Google Auth Library: Node.js Client to the list of supported Node.js packages. Hopefully that will help, I will give it a try as soon as it can be used :)
Update 02.12.20:
No progress so far, but I'm optimistic that the developers will add the package soon. I will post a final update as soon as I am done with this project.

Using Node request - How to authenticate on challenge based web page?

Exploring the 'request' package to perform some automated tasks at the office.
The requirement is to automatically log into a server (but there are many many), navigate to a web page and then collect information then create a report with it.
Thing is, these servers first ask for a user account, then they generate a challenge number that one would copy and paste into another server to be resolved as the challenge response. This response is pasted back into the web page I want the script to log in.
Trying to figure out how to go about doing this with 'request'. Following code only gets as far as looping through the page where to enter the user name:
"use strict"
var request = require('request');
var getJar = request.jar();
var opts = {
method: 'GET',
uri: 'https://serverIP/path/to/post/action',
agentOptions: {
rejectUnauthorized: false
},
jar: getJar,
followAllRedirects: true
};
var postJar = request.jar();
var postOpts = {
method: 'POST',
uri: 'https://serverIP/path/to/post/action',
form: {
userName: 'init'
},
agentOptions: {
rejectUnauthorized: false
},
postJar: postJar,
followAllRedirects: true,
headers: {
'Referer': 'value I see in dev console'
}
};
request(opts, function (err, res, body) {
console.log('GET error is: ' + JSON.stringify(err));
console.log('GET response is: ' + res);
console.log('GET body is: ' + body);
var cookie_string = getJar.getCookieString(opts.uri);
var cookies = getJar.getCookies(opts.uri);
console.log(JSON.stringify(cookies) + '\n' + cookie_string);
postOpts.postJar.setCookie(cookie_string, postOpts.uri);
request(postOpts, function (err, res, body) {
console.log('POST error is: ' + JSON.stringify(err));
console.log('POST response is: ' + res);
console.log('POST body is: ' + body);
});
});
The form parameters in the snippet are things I see in Firefox's developer console under Network > POST request > Params when I submit the user name through the browser.
The code is only taking me back to the page where the user name is requested. It kind of gets stuck there. I know this by logging the value of the POST body to console, it spits out the HTML code asking for that value, instead of the HTML where you see the challenge string and the input field where to paste the challenge response.
What may I do differently?
I used 'request-debug' package for further insight. Turned out, the first GET request happened successfully, from which I take the cookie=sessionId value, and inject it into the POST message jar as shown in my initial snippet.
With the debug on, I was able to detect a redirection to a URL saying 'cookiesRequired' at the end, request was following this redirection and generating a second GET to it, where I was shown HTML code with the message 'Invalid session you must be logged in'.
It then hit me that, the cookie-session system was not working properly. I saw from the second GET that it inserted a header with the session id taken from the cookie. So I injected this header into postJar like this:
var cookie_string = getJar.getCookieString(opts.uri);
console.log('ATTENTION--- The cookie from getJar is:\t' + cookie_string);
postOpts.headers.cookie = cookie_string;
This line doesnt even affect the behavior so far:
postOpts.jar.setCookie(cookie_string, '/');
After inserting the cookie header I am now getting prompted to resolve the next phase of the authentication.

Send CR LF in JavaScript string to node.js serialport server

I've successfully followed the instructions for creating a webpage to talk to serial ports found here. And here's the GitHub repository for his project. I've modified it slightly to use Windows COM ports and fake the Arduino data. Now I'm trying to modify it further to talk to one of my company's test boards. I've established two-way communication, so I know I can talk in both directions over the serial port.
Sending id?CRLF over serial to the board will get a response of something like id=91. I can do this in PuTTY by just typing in id? & hitting the Enter key, or in DockLight by creating a send sequence id?rn, both of which work as expected, I get the id=91 response.
However, in the client.js JavaScript, trying to send: socket.send("id?\r\n"); in the console doesn't work, but I see it show up with an extra line in the server response. So I see something like this:
Message received
id?
<=blank line
So I tried to send the ASCII equivalents by doing:
var id = String.fromCharCode(10,13);
socket.send("id?" + id);
Which also doesn't work, although two extra lines show up in the server.
Message received
id?
<=blank line
<=another blank line
EDIT: I've also tried: socket.send('id?\u000d\u000a'); Same results as the first Message received blurb above.
I see the sent command arrive at the server (I've modified it a bit to do a console.log upon receipt of a message from the client):
function openSocket(socket){
console.log('new user address: ' + socket.handshake.address);
// send something to the web client with the data:
socket.emit('message', 'Hello, ' + socket.handshake.address);
// this function runs if there's input from the client:
socket.on('message', function(data) {
console.log("Message received");
console.log(data);
//here's where the CRLF should get sent along with the id? command
myPort.write(data);// send the data to the serial device
});
// this function runs if there's input from the serialport:
myPort.on('data', function(data) {
//here's where I'm hoping to see the response from the board
console.log('message', data);
socket.emit('message', data); // send the data to the client
});
}
I'm not positive that the CRLF is the problem, but I'm pretty sure it is. Possibly it's being swallowed by the server?
How can I embed it in a string to be sent to the server so it get interpreted properly and sent along to the serial port?
Other SO pages I've read:
How can I insert new line/carriage returns into an element.textContent?
JavaScript string newline character?
Well, it turns out that the problem wasn't exactly the CRLF like I thought, it was how the string terminator was being handled. All of our devices use what we can an "S prompt" (s>) for when a command has been processed. When it's done the last thing the board does is return an S prompt, so I'd modified the original server parser code to look for that. However that's a response terminator, not a request terminator. Once I changed it back to parser: serialport.parsers.readline('\n') it started to work.
// serial port initialization:
var serialport = require('serialport'), // include the serialport library
SerialPort = serialport.SerialPort, // make a local instance of serial
portName = process.argv[2], // get the port name from the command line
portConfig = {
baudRate: 9600,
// call myPort.on('data') when a newline is received:
parser: serialport.parsers.readline('\n')
//changed from '\n' to 's>' and works.
//parser: serialport.parsers.readline('s>')
};

Sending data from JavaScript to Python function locally with AJAX

I am trying to build a website where a user can enter text, which will be picked up via javascript, and sent to a python function where it will be posted to twitter. For the time being, the python function is being stored locally, along with the rest of the site. However, my AJAX isn't too great and I'm having a few issues.
I have written AJAX code which sends a POST request to the python function with the tweet, and the response is the entire python script. No connection is made to the socket my script is listening to. Below is the AJAX function and the python script. Any ideas what's going on?
Thanks in advance for any help!
$(function(){
$('#PostTweet').on('click', function(e) {
var tweet = document.getElementById("theTweet").value;
var len = tweet.length;
if(len > 140){
window.alert("Tweet too long. Please remove some characters");
}else{
callPython(tweet);
}
});
});
function callPython(tweet){
window.alert("sending");
$.ajax({
type: "POST",
url: "tweet.py",
data: tweet,
success: function(response){
window.alert(response);
}
})
}
And the Python Script:
from OAuthSettings import settings
import twitter
from socket import *
consumer_key = settings['consumer_key']
consumer_secret = settings['consumer_secret']
access_token_key = settings['access_token_key']
access_token_secret = settings['access_token_secret']
s = socket()
s.bind(('', 9999))
s.listen(4)
(ns, na) = s.accept()
def PostToTwits(data):
try:
api = twitter.Api(
consumer_key = consumer_key,
consumer_secret = consumer_secret,
access_token_key = access_token_key,
access_token_secret = access_token_secret)
api.PostUpdate(data)
makeConnection(s)
except twitter.TwitterError:
print 'Post Unsuccessful. Error Occurred'
def makeConnection(s):
while True:
print "connected with: " + str(na)
try:
data = ns.recv(4096)
print data
PostToTwits(data)
except:
ns.close()
s.close()
break
makeConnection(s)
Your problem is that you are working with pure sockets which know nothing about HTTP protocol. Take a look at Flask or Bottle web micro frameworks to see how to turn python script or function into web endpoint.
you need a webserver so that your can make request via web browser.
you can web framework like flask or django or you can use webpy.
A simple example using webpy from their website
import web
urls = (
'/(.*)', 'hello'
)
app = web.application(urls, globals())
class hello:
def GET(self, name):
if not name:
name = 'World'
return 'Hello, ' + name + '!'
if __name__ == "__main__":
app.run()
then you call url(your python function) from javascript.
You can totally write a simple web server using sockets, and indeed you've done so. But this approach will quickly get tedious for anything beyond a simple exercise.
For example, your code is restricted to handling a single request handler, which goes to the heart of your problem.
The url on the post request is wrong. In your setup there is no notion of a url "tweet.py". That url would actually work if you were also serving the web page where the jquery lives from the same server (but you can't be).
You have to post to "http://localhost:9999" and you can have any path you want after:"http://localhost:9999/foo", "http://localhost:9999/boo". Just make sure you run the python script from the command line first, so the server is listening.
Also the difference between a get and a post request is part of the HTTP protocol which your simple server doesn't know anything about. This mainly means that it doesn't matter what verb you use on the ajax request. Your server listens for all HTTP verb types.
Lastly, I'm not seeing any data being returned to the client. You need to do something like ns.sendall("Some response"). Tutorials for building a simple http server abound and show different ways of sending responses.

node.js - should socket.get work on client?

I'm new to node.js and I'm making a simple chat app to get started. I'm a bit confused with the socket.set and socket.get methods.
The way the app works, is - first the client sets its username (pseudo):
function setPseudo() {
if ($("#pseudoInput").val() != "")
{
socket.emit('setPseudo', $("#pseudoInput").val());
$('#chatControls').show();
$('#pseudoInput').hide();
$('#pseudoSet').hide();
}
}
On the server, the pseudo is fetched with:
socket.on('setPseudo', function (data) {
socket.set('pseudo', data);
});
If I understand correctly, the server sets the pseudo variable with data received from this particular client. The server can later get that variable with socket.get. The following code broadcasts a message from a client to all clients:
socket.on('message', function (message) {
socket.get('pseudo', function (error, name) {
var data = { 'message' : message, pseudo : name };
socket.broadcast.emit('message', data);
console.log("user " + name + " send this : " + message);
})
});
What I don't understand is, why can't the client itself use socket.get to fetch its own pseudo? Using socket.get('pseudo') gives an error saying socket.get is not a function. Or am I overcomplicating this, and it would be better to just store the pseudo in a hidden field on the client or something similar? It just feels strange that a client should have to get its own username from the server.
EDIT:
Upon clicking Send, this code displays the sent message on the client itself. However, the displayed username is "Me". How can I modify it to show the client's username from the server?
addMessage($('#messageInput').val(), "Me", new Date().toISOString(), true);
function addMessage(msg, pseudo) {
$("#chatEntries").append('<div class="message"><p>' + pseudo + ' : ' + msg + '</p></div>');
}
You have to realize that although socket is a name used by both the server and the client, and interfaces are similar these are two independent things. (i.e. server socket and client socket) describing two ends of one connection.
If server sets some data on a socket what it actually does is it saves some data in its own memory and remembers that this data is associated with the socket. So how would client read something from server's memory? How can machine A read data from machine's B memory? Well, the only (reasonable) possibility is to send that data over network and this is actually what happens.
As for the other question: it's actually natural for the client to get its own name from the server or at least validate that name. Consider this scenario: two clients connect to the server and use the same name. This would lead to a conflict so it is up to the server to solve the problem. Basically you would tell one of the clients "sorry, this name is already being used, use something else".

Categories

Resources