I am new to Python and MongoDB environment. I am implementing a small application in Python with Tornado + MongoDB. I want to store sessions in mongoDB.
In server.py
import os
import tornado.web
import tornado.ioloop
import settings
application = tornado.web.Application(
settings.urls,
my_template_path,
my_static_path
)
if __nam__ == '__main__':
application.listen(8000)
tornado.ioloop.IOLoop.instance().start()
In settings.py
import handler as H
urls = [
(r"/", H.ShowHome),
(r"/login", H.ShowLogin),
(r"/dashboard", H.ShowDashboard)
]
In handler.py
import os
# import session or some library ????
class ShowHome(tornado.web.RequestHandler):
SESSION = False
def initialize(self):
#
# check session related things here
#
# self.SESSION = True or False based on session cookie
def get(self):
if not self.SESSION:
self.redirect('/login')
else:
self.render('index.html')
class ShowLogin(tornado.web.RequestHandler):
SESSION = False
def initialize(self):
#
# check session related things here
#
# self.SESSION = True or False based on session cookie
def get(self):
if self.SESSION:
self.redirect('/dashboard')
else:
self.render('login.html')
class ShowDashboard(tornado.web.RequestHandler):
SESSION = False
def initialize(self):
#
# check session related things here
#
# self.session = True or False based on session cookie
def get(self):
if not SESSION:
self.redirect('/login')
else:
self.render('dashboard.html')
In handlers I want to check session, how do I do this?
Do you mean something like this?
class Base(tornado.web.RequestHandler):
def get_unique_id(self):
return self.get_secure_cookie('unique_id')
def set_unique_id(self, some_value):
return self.set_secure_cookie('unique_id', some_value)
class ShowLogin(Base):
def get(self):
if get_unique_id():
# Get stuff from Mongo using unique_id
# mongo calls
self.redirect('/dashboard')
else:
self.render('login.html')
class LoginLogin(Base):
def post(self):
self.set_unique_id(some_id)
You probably dont want to do this though - Let Tornado handle if someone is logged in or out with the authenticated decorator
And unless you are holding a lot of data or very sensitive data it's normal (and easier) to put session data in the cookies
Related
i am trying to generate Flask route using a basic DI i.e mapping methods as route handlers, i am a total beginner at Flask so mind my basic skills
class myClass():
def __init__(self):
self.dbConnObj = DbToolsMySql('someconnection', 'slave')
self.dbConnObj.connect()
self.blueprint = Blueprint('myClass', __name__)
self.blueprint.add_url_rule('/my_method', view_func=self.my_method)
def my_method(self, event):
retun "hello"
and then in my handler file
from flask import Flask
from flask_restful import Api, Resource
from src.app.services.myClassimport myClass
app = Flask(__name__)
app.register_blueprint(myClass.blueprint)
if __name__ == "main":
app.run()
Quite simple ehh???? but not working... i am getting following message
Not Found The requested URL was not found on the server. If you
entered the URL manually please check your spelling and try again.
typically you add routes to the Flask app with decorators like so:
app = Flask(__name__)
#app.route('/some-endpoint')
def some_endpoint_handler():
# do something
pass
Or you can add without a decorator like so:
def some_endpoint_handler():
# do something
pass
app = Flask(__name__)
app.route('/some-endpoint', methods=['GET'])(some_endpoint_handler)
So in your scenario, you can pass the app.route call to your myClass object and set the route like this:
class myClass():
def __init__(self, router):
self.dbConnObj = DbToolsMySql('someconnection', 'slave')
self.dbConnObj.connect()
self.blueprint = Blueprint('myClass', __name__)
#self.blueprint.add_url_rule('/my_method', view_func=self.my_method)
router('/my_method', ['GET'])(self.my_method)
def my_method(self, event):
retun "hello"
myObj = myClass( app.route )
or, invert the dependency:
app = Flask(__name__)
#app.route(myClass.blueprint.some_endpoint_string)
def some_endpoint_handler():
myClass.blueprint.call_some_endpoint_handler()
pass
if __name__ == "main":
app.run()
Problem:
So my problem is I have a Flask microservice want to implement the unit tests to it so when I start writing my test cases I found that I need to authenticate the unit test client because of some endpoints need authorization and here comes the problem the whole authentication system in another service this service all can do about the authentication is to validate the JWT token and get user ID from it so here is one of the views.py
from flask_restful import Resource
from common.decorators import authorize
class PointsView(Resource):
decorators = [authorize]
def get(self, user):
result = {"points": user.active_points}
return result
and authorize decorator from decorators.py
import flask
import jwt
from jwt.exceptions import DecodeError, InvalidSignatureError
from functools import wraps
from flask import request
from flask import current_app as app
from app import db
from common.models import User
from common.utils import generate_error_response
def authorize(f):
"""This decorator for validate the logged in user """
#wraps(f)
def decorated_function(*args, **kwargs):
if 'Authorization' not in request.headers:
return "Unable to log in with provided credentials.", 403
raw_token = request.headers.get('Authorization')
if raw_token[0:3] != 'JWT':
return generate_error_response("Unable to log in with provided credentials.", 403)
token = str.replace(str(raw_token), 'JWT ', '')
try:
data = jwt_decode_handler(token)
except (DecodeError, InvalidSignatureError):
return generate_error_response("Unable to log in with provided credentials.", 403)
user = User.query.filter_by(id=int(data['user_id'])).first()
return f(user, *args, **kwargs)
return decorated_function
and the test case from tests.py
import unittest
from app import create_app, db
from common.models import User
class TestMixin(object):
"""
Methods to help all or most Test Cases
"""
def __init__(self):
self.user = None
""" User Fixture for testing """
def user_test_setup(self):
self.user = User(
username="user1",
active_points=0
)
db.session.add(self.user)
db.session.commit()
def user_test_teardown(self):
db.session.query(User).delete()
db.session.commit()
class PointsTestCase(unittest.TestCase, TestMixin):
"""This class represents the points test case"""
def setUp(self):
"""Define test variables and initialize app."""
self.app = create_app("testing")
self.client = self.app.test_client
with self.app.app_context():
self.user_test_setup()
def test_get_points(self):
"""Test API can create a points (GET request)"""
res = self.client().get('/user/points/')
self.assertEqual(res.status_code, 200)
self.assertEquals(res.data, {"active_points": 0})
def tearDown(self):
with self.app.app_context():
self.user_test_teardown()
# Make the tests conveniently executable
if __name__ == "__main__":
unittest.main()
My authentication system work as the following:
Any service (include this one) request User service to get user JWT
token
Any service take the JWT token decoded and get the user ID
from it
Get the user object from the database by his ID
so I didn't know how to make the authentication flow in the test cases.
Here is just an example. I skipped some little things such as create_app, jwt.decode(token) etc. I'm sure you can understand the main approach. Structure:
src
├── __init__.py # empty
├── app.py
└── auth_example.py
app.py:
from flask import Flask
from src.auth_example import current_identity, authorize
app = Flask(__name__)
#app.route('/')
#authorize()
def main():
"""
You can use flask_restful - doesn't matter
Do here all what you need:
user = User.query.filter_by(id=int(current_identity['user_id'])).first()
etc..
just demo - return current user_id
"""
return current_identity['user_id']
auth_example.py:
from flask import request, _request_ctx_stack
from functools import wraps
from werkzeug.local import LocalProxy
current_identity = LocalProxy(lambda: getattr(_request_ctx_stack.top, 'current_identity', None))
def jwt_decode_handler(token):
"""
just do here all what you need. Should return current user data
:param str token:
:return: dict
"""
# return jwt.decode(token), but now - just demo
raise Exception('just demo')
def authorize():
def _authorize(f):
#wraps(f)
def __authorize(*args, **kwargs):
if 'Authorization' not in request.headers:
return "Unable to log in with provided credentials.", 403
raw_token = request.headers.get('Authorization')
if raw_token[0:3] != 'JWT':
return "Unable to log in with provided credentials.", 403
token = str.replace(str(raw_token), 'JWT ', '')
try:
# I don't know do you use Flask-JWT or not
# this is doesn't matter - all what you need is just to mock jwt_decode_handler result
_request_ctx_stack.top.current_identity = jwt_decode_handler(token)
except Exception:
return "Unable to log in with provided credentials.", 403
return f(*args, **kwargs)
return __authorize
return _authorize
Our test:
import unittest
from mock import patch
from src.app import app
app.app_context().push()
class TestExample(unittest.TestCase):
def test_main_403(self):
# just a demo that #authorize works fine
result = app.test_client().get('/')
self.assertEqual(result.status_code, 403)
def test_main_ok(self):
expected = '1'
# we say that jwt_decode_handler will return {'user_id': '1'}
patcher = patch('src.auth_example.jwt_decode_handler', return_value={'user_id': expected})
patcher.start()
result = app.test_client().get(
'/',
# send a header to skip errors in the __authorize
headers={
'Authorization': 'JWT=blabla',
},
)
# as you can see current_identity['user_id'] is '1' (so, it was mocked in view)
self.assertEqual(result.data, expected)
patcher.stop()
So, in your case you need just mock jwt_decode_handler. Also I recommend do not add any additional arguments inside a decorators. It will be hard to debugging when you have more than two decorators with a different arguments, recursion, hard processing etc.
Hope this helps.
Could you create some mock tokens in your unit testing framework (that your decorator can actually decode like in a real request) and send them in with your test client? An example of how that might look can be seen here: https://github.com/vimalloc/flask-jwt-extended/blob/master/tests/test_view_decorators.py#L321
I am new to python , I have a class and it has two routes defined on it. How do I bind those routes to the main flask server running.
Here is my restinput.py
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import logging
from rasa_core.channels import HttpInputChannel
from rasa_core import utils
from rasa_core.agent import Agent
from rasa_core.interpreter import RasaNLUInterpreter
from rasa_core.channels.channel import UserMessage
from rasa_core.channels.direct import CollectingOutputChannel
from flask import Blueprint, request, jsonify
logger = logging.getLogger(__name__)
class RestInput(InputChannel):
#classmethod
def name(cls):
return "rest"
#staticmethod
def on_message_wrapper(on_new_message, text, queue, sender_id):
collector = QueueOutputChannel(queue)
message = UserMessage(text, collector, sender_id,
input_channel=RestInput.name())
on_new_message(message)
queue.put("DONE")
def _extract_sender(self, req):
return req.json.get("sender", None)
def _extract_message(self, req):
return req.json.get("message", None)
def stream_response(self, on_new_message, text, sender_id):
from multiprocessing import Queue
q = Queue()
t = Thread(target=self.on_message_wrapper,
args=(on_new_message, text, q, sender_id))
t.start()
while True:
response = q.get()
if response == "DONE":
break
else:
yield json.dumps(response) + "\n"
def blueprint(self, on_new_message):
custom_webhook = Blueprint(
'custom_webhook_{}'.format(type(self).__name__),
inspect.getmodule(self).__name__)
#custom_webhook.route("/", methods=['GET'])
def health():
return jsonify({"status": "ok"})
#custom_webhook.route("/webhook", methods=['POST'])
def receive():
sender_id = self._extract_sender(request)
text = self._extract_message(request)
should_use_stream = utils.bool_arg("stream", default=False)
if should_use_stream:
return Response(
self.stream_response(on_new_message, text, sender_id),
content_type='text/event-stream')
else:
collector = CollectingOutputChannel()
on_new_message(UserMessage(text, collector, sender_id,
input_channel=self.name()))
return jsonify(collector.messages)
return custom_webhook
def run(serve_forever=True):
interpreter = RasaNLUInterpreter("models/nlu/default/current")
action_endpoint = EndpointConfig(url="http://localhost:5055/webhook")
agent = Agent.load("models/dialogue", interpreter=interpreter,action_endpoint=action_endpoint)
input_channel = RestInput()
if serve_forever:
agent.handle_channels([InputChannel(5004, "/chat", input_channel)])
return agent
This is my app.py
from flask import Flask
from flask_cors import CORS
from restinput import RestInput
app = Flask(__name__)
CORS(app)
#app.route("/")
def index():
return "Hello"
#app.route("/parse",methods=['POST'])
def chat():
#some code to access the class on this route
if __name__ == "__main__":
app.run(host='0.0.0.0')
What should I add in the "/parse" route , to be able to access the routes from the RestInput class.
I tried creating the agent in the /parse route and then accessing the other routes like the /parse/webhook , but it is not working. I have gone through the flask blueprint and view but I am unable to figure it out.
Routes in RestInput are attached to a Blueprint. To attach those routes to your main app you should register a blueprint:
# app.py
some_function = ... # whatever you want
app.register_blueprint(RestInput().blueprint(some_function))
Can I change database name defined in my Application class like below
or
What is right approach to change database name dynamically in Tornado?
class Application(tornado.web.Application):
def __init__(self):
self.db = "test"
In one of my Request Handler using value from args
class MainHandler(tornado.web.RequestHandler):
def initialize(self, database):
self.database = database
self.db = "new_test"
If by "dynamically" you mean you can modify it for different handlers, you can pass it to your URLSpec:
from tornado.web import url
from myhandlers import MyHandler
urls_list = [
url('/foo/bar', MyHandler, kwargs={'database': my_database}),
]
app = Application(urls_list)
How I store an instance of a connection in twisted.web? I have seen request.getSession() but I searched and there are very few examples of how it is stored and retrieved later.
Thanks.
Update:
I want to store the ldap connection in a session for retrieve later
def render_POST(self, request):
command = request.path[1:]
session = request.getSession()
if command == "authentication":
Connect = LdapConnection(request.args['host'][0],request.args['user'][0],request.args['password'][0])
session.addComponent(LdapConnection, Connect)
if command == "users":
Connect = session.getComponent(LdapConnection)
u = Users(Connect, request.args['name'][0],request.args['employeeNumber'])
return
There are plenty of examples in the documentation of twisted. If you prefer a quick summary on how to use sessions.
from twisted.web.resource import Resource
class ShowSession(Resource):
def render_GET(self, request):
return 'Your session id is: ' + request.getSession().uid
class ExpireSession(Resource):
def render_GET(self, request):
request.getSession().expire()
return 'Your session has been expired.'
resource = ShowSession()
resource.putChild("expire", ExpireSession())
Do not forget that request.getsession() will create the session if it doesn't already exists. This tutorial explains how to store objects in session.
cache()
from zope.interface import Interface, Attribute, implements
from twisted.python.components import registerAdapter
from twisted.web.server import Session
from twisted.web.resource import Resource
class ICounter(Interface):
value = Attribute("An int value which counts up once per page view.")
class Counter(object):
implements(ICounter)
def __init__(self, session):
self.value = 0
registerAdapter(Counter, Session, ICounter)
class CounterResource(Resource):
def render_GET(self, request):
session = request.getSession()
counter = ICounter(session)
counter.value += 1
return "Visit #%d for you!" % (counter.value,)
resource = CounterResource()