How to drop a connection with flask - python

Say, I have this tiny flask app if the user has a valid token when he posts something I give him something else. When he doesn't I want to silently drop his connection. How do I do that?
Here's some sample code to help you visualize what I want to do
#app.route("/do_action", methods=('POST',))
def do_action():
if request.form['TOKEN'] not in valid_tokens:
drop_connection() # how?
else:
return get_action_result()
To be clear, I don't want to throw an error or politely close the connection I want to just drop the user request, let him hit the timeout.

if you have logout method implemented in your application, You can direct the user to logout return redirect("url for logout")

Related

Flask Flash Message Persists on Reload

When I hit refresh on a page with flash message already showing, the message persist even after the refresh. How do I stop the message from showing on refresh? I noticed when I use render_template after flash the flash message will persist, but when I use redirect it doesn't. However I have to pass a non-3xx status code when I flash a message, using redirect only gets user wait on a redirection page until further action, which is not desirable either.
Method 1: Shows the right page but message persists on refresh:
flash("An error occurred.")
return render_template("page.html"), 400
Method 2: Stuck on redirection page but message doesn't persist on refresh:
flash("An error occurred.")
return redirect(url_for('show-page'), code=400)
Redirecting...
You should be redirected automatically to target URL: /page. If not click the link.
Method 2 does work as I intended if I pass a 3xx code, but I need to pass non-3xx code.
Note: The user should be on the same page throughout.
In my case, the flash message pops up to give users feedback immediately after they submit a form. The message persisted after refresh because the refresh triggered a double submit of the form. Redirect prevents the double submit problem and subsequently the persistent message. I read another post on the status code. Since this is all user-facing rather than a REST API, status code doesn't really matter, I went with method 2 with a 3xx default status code:
flash("An error occurred.")
return redirect(url_for('show-page'))
In my case all the messages were stuck in session when using the stream_template and when changing to render_template the messages were removed from the session normally.
#app.route("/startpage", methods=['GET', 'POST'])
def startpage():
if validate_token() == True:
flashes = session.get('_flashes', [])
print(flashes)
#return stream_template('start_page.html', title="Inicio")
return render_template('start_page.html', title="Inicio")
else:
return redirect(url_for('index'))
I did some tests and in my case it really was that. I didn't find anything in the Flask documentation talking about this difference in stream_template and render_template.

How to wait in django until a request for endpoint arrives?

Tell me with what you can wait for a response to another endpoint?
I am on the main page (index), entering something into the form. The POST request is sent to another server. At this moment:
another server processes the data and, depending on their correctness, makes a POST request to my url /answer (True or False).
I will be redirected, for example, to another page.
How to register the logic of another page (another) so that Django waits for a POST request from another server to /answer and depending on this request True/False, I output everything OK or everything Bad on this page?
url.py
urlpatterns = [
path('index/', index, name='index'),
path('page_2/', page_2, name='page_2'),
path('answer/', answer, name='answer'),
]
-------------------------------------------------
views.py
def index(request):
requests.post(example.com, data='My data')
return redirect('page_2')
def page_2(request):
# wait request in answer
if request.session['answer'] is True:
return 'Ok'
retunr 'Bad'
def answer(request):
data = request.data
# send to page_2 or save in request.session['answer']
return Response(status=200)
I reckon it's a strange situation and it's better if you could redesign the logic of your code so that the view functions process the request ASAP and not busily wait for external events to be triggered as it increases response time.
However, in order to achieve this purpose we need a communication channel between index and answer view. So to implement a communication like this:
index: Hey answer! I've sent the request. I'm going to sleep, wake me up if you got its result.
answer: Oh I got it man. Here you are. Wake up!
index: Thanks. Now I process it and return my response.
So this channel might be anything! A model in database, some entities in redis, some files in filesystem, etc.
One possible solution using the models might be:
Create a model(name it ExampleRequest for example) consisting of a boolean field named received
In index view, create an instance of ExampleRequest with received = False before sending the request.
In answer view, find the previously created ExampleRequest and set its received field to True
In index view, after sending the request, in a while loop, query the database and check if the created ExampleRequest instance has received = True? If yes, then the external server has called answer. So break the while and do the rest of the work; otherwise, just time.sleep(1) and continue the while loop.
Just note:
When multiple clients are using your website, some of them might request index view and then there will be more than one instance of ExampleRequest. In answer view, you have to be able to find out the current request is related to which one of those instances. You might need to store a unique data related to that request in ExampleRequest model.
You might consider the situation where the other server doesn't call answer view ever. So there might be an upper bound for the iterations of index view's while loop.
Also you may remove ExampleRequest instances after capturing them in index view in order to optimize disk usage of your database.
I say it again, it's better if you can do the polling stuff in frontend instead of backend to avoid high response time and other syncing issues.
This might not the complete answer, but it gives you way.
def index(request):
requests.post(example.com, data='My data')
return redirect('page_2')
Change it to following
import httpx
async def index(request):
async with httpx.AsyncClient() as client:
response = await client.post(example.com, data='My data')
print(response.json())

Jupyterhub Custom Authenticator

I am a little stuck with writing a custom authenticator for jupyterhub. Most probably because I do not understand the inner workings of the available REMOTE_USER authenticator. I am not sure if it is applicable in my case... anyhow... this is what I'd like to do:
My general idea: I have a server that authenticates a user with his or her institutional login. After logging into the institution server/website, the users' data are encoded -- only some details to identify the user. They are then redirected to a the jupyterhub domain in the following way
https://<mydomain>/hub/login?data=<here go the encrypted data>
Now, if a request gets sent like this to my jupyterhub-domain, I'd like to decrypt the submitted data, and authenticate the user.
My trial:
I tried it with the following code. But it seems I am too nooby... :D
So please, pedantic comments are welcome :D
from tornado import gen
from jupyterhub.auth import Authenticator
class MyAuthenticator(Authenticator):
login_service = "my service"
authenticator_login_url="authentication url"
#gen.coroutine
def authenticate(self,handler,data=None):
# some verifications go here
# if data is verified the username is returned
My first problem... clicking the button on the login page, doesn't redirect me to my Authentication URL... it seems the variable authenticator_login_url from the login template is set somewhere else...
Second problem... a request made to .../hub/login?data=... is not evaluated by the authenticator (it seems...)
So: Has somebody any hints for me how to go about this?
As you see I followed the tutorials here:
https://universe-docs.readthedocs.io/en/latest/authenticators.html
So the following code does the job, however, I am always open to improvements.
So, what I did was redirect an empty login attempt to the login-url and deny access. If data is presented, check the validity of the data. If verified, user can login.
from tornado import gen, web
from jupyterhub.handlers import BaseHandler
from jupyterhub.auth import Authenticator
class MyAuthenticator(Authenticator):
login_service = "My Service"
#gen.coroutine
def authenticate(self,handler,data=None):
rawd = None
# If we receive no data we redirect to login page
while (rawd is None):
try:
rawd = handler.get_argument("data")
except:
handler.redirect("<The login URL>")
return None
# Do some verification and get the data here.
# Get the data from the parameters send to your hub from the login page, say username, access_token and email. Wrap everythin neatly in a dictionary and return it.
userdict = {"name": username}
userdict["auth_state"] = auth_state = {}
auth_state['access_token'] = verify
auth_state['email'] = email
#return the dictionary
return userdict
Simply add the file to the Python path, so that Jupyterhub is able to find it and make the necessary configurations in your jupyterhub_config.py file.

Tornado Websocket "error: Authentication missing"

So I've got redis feature and tornado running on my server and whenever I open my websocket chat through a login, the terminal displays the following message
Error: Authentication missing
I'm not sure why this is happening because there are cookies in the authentication part of the app,
# Save user when authentication was successful.
def on_user_find(result, user=user):
##todo: We should check if email is given even though we can assume.
if result == "null" or not result:
# If user does not exist, create a new entry.
self.application.client.set("user:" + user["email"], tornado.escape.json_encode(user))
else:
# Update existing user.
# #todo: Should use $set to update only needed attributes?
dbuser = tornado.escape.json_decode(result)
dbuser.update(user)
user = dbuser
self.application.client.set("user:" + user["email"], tornado.escape.json_encode(user))
# Save user id in cookie.
self.set_secure_cookie("user", user["email"])
self.application.usernames[user["email"]] = user.get("name") or user["email"]
And in the websocket.py (where I run the script) I've made it so that the websocket handle checks if there are cookies available first before user access the app,
class ChatSocketHandler(tornado.websocket.WebSocketHandler):
def open(self, user):
self.login = self.get_secure_cookie("user")
if not self.login:
# self.login = "anonymous"
print "Not authorized"
self.disconnect()
return
Yet it's still displaying the error, I've searched online and checked several SO answers but they don't show any solid solution in regards to this question. So far the most I've gotten is that I have to access the websocket header to put the above code inside, but I have no clue how I would do that. Help?

Missing OAuth request token cookie error using tornado and TwitterMixin

I'm using tornado and the TwitterMixin and I use the following basic code:
class OauthTwitterHandler(BaseHandler, tornado.auth.TwitterMixin):
#tornado.web.asynchronous
def get(self):
if self.get_argument("oauth_token", None):
self.get_authenticated_user(self.async_callback(self._on_auth))
return
self.authorize_redirect()
def _on_auth(self, user):
if not user:
raise tornado.web.HTTPError(500, "Twitter auth failed")
self.write(user)
self.finish()
For me it works very well but sometimes, users of my application get a 500 error which says:
Missing OAuth request token cookie
I don't know if it comes from the browser or the twitter api callback configuration.
I've looked through the tornado code and I don't understand why this error
appears.
Two reasons why this might happen:
Some users may have cookies turned off, in which case this won't work.
The cookie hasn't authenticated. It's possible that the oauth_token argument is set, but the cookie is not. Not sure why this would happen, you'd have to log some logging to understand why.
At any rate, this isn't an "error," but rather a sign the user isn't authenticated. Maybe if you see that you should just redirect them to the authorize URL and let them try again.
I found the solution !!
It was due to my DNS.
I didn't put the redirection for www.mydomain.com and mydomain.com so sometimes the cookie was set in www. and sometimes not then my server didn't check in the good place, didn't find the cookie and then send me a 500 error.
The reason this was happening to me is that the Callback URL configuration was pointing to a different domain.
Take a look at the settings tab for your application at https://dev.twitter.com/apps/ or if the users getting the error are accessing your site from a different domain.
See: http://groups.google.com/group/python-tornado/browse_thread/thread/55aa42eef42fa1ac

Categories