Wednesday, May 28, 2014

SockJS-Tornado : chat example - html part

<!DOCTYPE html>
<html>
<head>
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
  <script src="http://cdn.sockjs.org/sockjs-0.3.min.js"></script>
  <script>
    $(function() {
      var conn = null;

      function log(msg) {
        var control = $('#log');
        control.html(control.html() + msg + '<br/>');
        control.scrollTop(control.scrollTop() + 1000);
      }

      function connect() {
        disconnect();

        var transports = $('#protocols input:checked').map(function(){
            return $(this).attr('id');
        }).get();

        conn = new SockJS('http://' + window.location.host + '/chat', transports);

        log('Connecting...');

        conn.onopen = function() {
          log('Connected.');
          update_ui();
        };

        conn.onmessage = function(e) {
          log('Received: ' + e.data);
        };

        conn.onclose = function() {
          log('Disconnected.');
          conn = null;
          update_ui();
        };
      }

      function disconnect() {
        if (conn != null) {
          log('Disconnecting...');

          conn.close();
          conn = null;

          update_ui();
        }
      }

      function update_ui() {
        var msg = '';

        if (conn == null || conn.readyState != SockJS.OPEN) {
          $('#status').text('disconnected');
          $('#connect').text('Connect');
        } else {
          $('#status').text('connected (' + conn.protocol + ')');
          $('#connect').text('Disconnect');
        }
      }

      $('#connect').click(function() {
        if (conn == null) {
          connect();
        } else {
          disconnect();
        }

        update_ui();
        return false;
      });

      $('form').submit(function() {
        var text = $('#text').val();
        log('Sending: ' + text);
        conn.send(text);
        $('#text').val('').focus();
        return false;
      });
    });
</script>
</head>
<body>
<h3>Chat!</h3>
<div id="protocols" style="float: right">
  <ul>
    <li>
      <input id="websocket" type="checkbox" value="websocket" checked="checked"></input>
      <label for="websocket">websocket</label>
    </li>
    <li>
      <input id="xhr-streaming" type="checkbox" value="xhr-streaming" checked="checked"></input>
      <label for="xhr-streaming">xhr-streaming</label>
    </li>
    <li>
      <input id="iframe-eventsource" type="checkbox" value="iframe-eventsource" checked="checked"></input>
      <label for="iframe-eventsource">iframe-eventsource</label>
    </li>
    <li>
      <input id="iframe-htmlfile" type="checkbox" value="iframe-htmlfile" checked="checked"></input>
      <label for="iframe-htmlfile">iframe-htmlfile</label>
    </li>
    <li>
      <input id="xhr-polling" type="checkbox" value="xhr-polling" checked="checked"></input>
      <label for="xhr-polling">xhr-polling</label>
    </li>
    <li>
      <input id="iframe-xhr-polling" type="checkbox" value="iframe-xhr-polling" checked="checked"></input>
      <label for="iframe-xhr-polling">iframe-xhr-polling</label>
    </li>
    <li>
      <input id="jsonp-polling" type="checkbox" value="jsonp-polling" checked="checked"></input>
      <label for="jsonp-polling">jsonp-polling</label>
    </li>
  </ul>
</div>

<div>
  <a id="connect" href="#">Connect</a>&nbsp;|&nbsp;Status: <span id="status">disconnected</span>
</div>
<div id="log" style="width: 60em; height: 20em; overflow:auto; border: 1px solid black">
</div>
<form id="chatform">
  <input id="text" type="text" />
  <input type="submit" />
</form>
</body>
</html>

SockJS-Tornado : chat example.

# -*- coding: utf-8 -*-
"""
    Simple sockjs-tornado chat application. By default will listen on port 8080.
"""
import tornado.ioloop
import tornado.web


import sockjs.tornado




class IndexHandler(tornado.web.RequestHandler):
    """Regular HTTP handler to serve the chatroom page"""
    def get(self):
        self.render('index.html')




class ChatConnection(sockjs.tornado.SockJSConnection):
    """Chat connection implementation"""
    # Class level variable
    participants = set()


    def on_open(self, info):
        # Send that someone joined
        self.broadcast(self.participants, "Someone joined.")


        # Add client to the clients list
        self.participants.add(self)


    def on_message(self, message):
        # Broadcast message
        self.broadcast(self.participants, message)


    def on_close(self):
        # Remove client from the clients list and broadcast leave message
        self.participants.remove(self)


        self.broadcast(self.participants, "Someone left.")


if __name__ == "__main__":
    import logging
    logging.getLogger().setLevel(logging.DEBUG)


    # 1. Create chat router
    ChatRouter = sockjs.tornado.SockJSRouter(ChatConnection, '/chat')


    # 2. Create Tornado application
    app = tornado.web.Application(
            [(r"/", IndexHandler)] + ChatRouter.urls
    )


    # 3. Make Tornado app listen on port 8080
    app.listen(8080)


    # 4. Start IOLoop
    tornado.ioloop.IOLoop.instance().start()

Sunday, May 25, 2014

Tornado authentification example

https://github.com/contaconta/TornadoAuthTest

# -*- coding: utf-8 -*-

'''
    User: ogata
    Date: 5/31/12
    Time: 2:10 PM
'''
__author__ = 'ogata'

import tornado.ioloop
import tornado.web
import tornado.escape
import tornado.options
from tornado.options import define, options

import os
import logging

define("port", default=5000, type=int)
define("username", default="user")
define("password", default="pass")


class Application(tornado.web.Application):

    def __init__(self):
        handlers = [
            (r'/', MainHandler),
            (r'/auth/login', AuthLoginHandler),
            (r'/auth/logout', AuthLogoutHandler),
        ]
        settings = dict(
            cookie_secret='gaofjawpoer940r34823842398429afadfi4iias',
            static_path=os.path.join(os.path.dirname(__file__), "static"),
            template_path=os.path.join(os.path.dirname(__file__), "templates"),
            login_url="/auth/login",
            xsrf_cookies=True,
            autoescape="xhtml_escape",
            debug=True,
            )
        tornado.web.Application.__init__(self, handlers, **settings)

class BaseHandler(tornado.web.RequestHandler):

    cookie_username = "username"

    def get_current_user(self):
        username = self.get_secure_cookie(self.cookie_username)
        logging.debug('BaseHandler - username: %s' % username)
        if not username: return None
        return tornado.escape.utf8(username)

    def set_current_user(self, username):
        self.set_secure_cookie(self.cookie_username, tornado.escape.utf8(username))

    def clear_current_user(self):
        self.clear_cookie(self.cookie_username)


class MainHandler(BaseHandler):

    @tornado.web.authenticated
    def get(self):
        self.write("Hello, <b>" + self.get_current_user() + "</b> <br> <a href=/auth/logout>Logout</a>")


class AuthLoginHandler(BaseHandler):

    def get(self):
        self.render("login.html")

    def post(self):
        logging.debug("xsrf_cookie:" + self.get_argument("_xsrf", None))

        self.check_xsrf_cookie()

        username = self.get_argument("username")
        password = self.get_argument("password")

        logging.debug('AuthLoginHandler:post %s %s' % (username, password))

        if username == options.username and password == options.password:
            self.set_current_user(username)
            self.redirect("/")
        else:
            self.write_error(403)


class AuthLogoutHandler(BaseHandler):

    def get(self):
        self.clear_current_user()
        self.redirect('/')


def main():
    tornado.options.parse_config_file(os.path.join(os.path.dirname(__file__), 'server.conf'))
    tornado.options.parse_command_line()
    app = Application()
    app.listen(options.port)
    logging.debug('run on port %d in %s mode' % (options.port, options.logging))
    tornado.ioloop.IOLoop.instance().start()

if __name__ == "__main__":
    main()

Friday, May 23, 2014

Tornado – XSRF

Quote from site
XSRF or CSRF or sea-surf is web security vulnerability that takes advantage of website’s trust in user. In this attack, the user is forced into performing unwanted activities on the website where s/he is logged in.
http://technobeans.wordpress.com/2012/08/31/tornado-xsrf/

Tornado user authentication example

Another example from mehmetkose
https://github.com/mehmetkose/tornado-user-authentication-example/blob/master/app.py
import tornado.httpserver
import tornado.ioloop
import tornado.web
import tornado.options
import os.path
from tornado.options import define, options

define("port", default=8000, help="run on the given port", type=int)

class BaseHandler(tornado.web.RequestHandler):
 def get_current_user(self):
  return self.get_secure_cookie("user")
  
class MainHandler(BaseHandler):
 @tornado.web.authenticated
 def get(self):
  self.render('index.html', user=self.current_user)

class LoginHandler(BaseHandler):
 def get(self):
  self.render('login.html')
 def post(self):
  getusername = self.get_argument("username")
  getpassword = self.get_argument("password")
  if "demo" == getusername and "demo" == getpassword:
      self.set_secure_cookie("user", self.get_argument("username"))
      self.redirect("/")
  else:
      wrong=self.get_secure_cookie("wrong")
      if wrong==False or wrong == None:
          wrong=0  
      self.set_secure_cookie("wrong", str(int(wrong)+1))
      self.write('Kullanici Adi veya Sifre Yanlis <a href="/login">Geri</a> '+str(wrong))

class LogoutHandler(BaseHandler):
    def get(self):
        self.clear_cookie("user")
        self.redirect(self.get_argument("next", "/"))

class Application(tornado.web.Application):
    def __init__(self):
        base_dir = os.path.dirname(__file__)
        settings = {
            "cookie_secret": "bZJc2sWbQLKos6GkHn/VB9oXwQt8S0R0kRvJ5/xJ89E=",
            "login_url": "/login",
   'template_path': os.path.join(base_dir, "templates"),
   'static_path': os.path.join(base_dir, "static"),
   'debug':True,
   "xsrf_cookies": True,
  }
  
        tornado.web.Application.__init__(self, [
            tornado.web.url(r"/", MainHandler, name="main"),
            tornado.web.url(r'/login', LoginHandler, name="login"),
            tornado.web.url(r'/logout', LogoutHandler, name="logout"),
        ], **settings)

def main():
    tornado.options.parse_command_line()
    Application().listen(options.port)
    tornado.ioloop.IOLoop.instance().start()

if __name__ == "__main__":
    main()

Tornado authentication best practice

Here is an extract of the Google group discussion
https://groups.google.com/forum/#!msg/python-tornado/ty8Xerv0ALc/IQRMM9n_4agJ
Response from Ben:
I think the author's argument against persistent login is misplaced; persistent login cookies are compatible with the security needs of most sites.  However, the points he make here are valid and are best practices be followed whether your login cookies are persistent or not.  (Tornado's example apps don't do this.  I'd like to do a better job of steering people towards best practices, but I also don't want to overwhelm the simple examples with complex login functionality).  

Specifically, you should store a random session key for each user in your database and include this session key in your signed cookies.  The session key would be deleted or reset to a new random value when the user explicitly logs out or changes their password, and cookies would be rejected if they didn't have the right session key.

Storing only a hash of the session key in the database is a good idea, although this means that you have to have a one-to-many relationship between users and session keys (unless you want a login on one browser to invalidate any current logins on other browsers), since you won't be able to reuse the previous session key the next time the user logs in.  The recommendation to use bcrypt or equivalent is kind of silly, though - a sufficiently large random session key cannot be brute forced even if the hash is a fast one, so just use your preferred SHA-family hash (bcrypt and friends are for when you're using low-entropy keys so that humans can remember them).  Also note that it's important to use a good cryptographic random number generator (e.g. os.urandom instead of anything from the python stdlib's random module).  

Server-side session keys ensure that even if a cookie is stolen (which can happen for non-persistent cookies too) it can be invalidated as soon as the user logs out, instead of being usable until it expires.  Hashing the session keys in the database means that even an attacker who has read-only access to your database and a copy of your cookie-signing key cannot forge login cookies for arbitrary users.  

-Ben

Tornado - Authentication

Quote from site :

Tornado provides get_current_user() method to determine if the user is already logged in. Developers need to override this method to get the current user and that can be done through cookies (secure). Every logged-in user, is represented by Tornado as self.current_user. By default this value is set to None.

http://technobeans.wordpress.com/2012/08/14/tornado-authentication/
import tornado.ioloop
import tornado.web

class Main(tornado.web.RequestHandler):
        def get_current_user(self):
                return self.get_secure_cookie("user")

        def get(self):
                if not self.current_user:
                        self.redirect("/login")
                        return
                username = self.current_user
                self.write('Hi there, '+ username)

class Login(Main):
        def get(self):
                self.render('auth.html')
        def post(self):
                self.set_secure_cookie("user", self.get_argument("username"))
                self.redirect("/")

application = tornado.web.Application([
        (r"/", Main),
        (r"/login", Login),
        (r"/(style\.css)",tornado.web.StaticFileHandler, {"path": "./css/"}),
        ],debug=True, cookie_secret="61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=")

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

Tornado Sqlite3

Quote fom site :
It allows you to build Tornado apps that work with SQLite3
using the same API that Tornado provides in its database module.
https://github.com/shamiksharma/tornado_sqlite3 

Tornado-Subprocess

Quote from site :
A module which allows you to spawn subprocesses from a tornado web application in a non-blocking fashion.

https://github.com/vukasin/tornado-subprocess

Example:

def print_res( status, stdout, stderr, has_timed_out ) :
     if status == 0:
         print "OK:"
         print stdout
     else:
         print "ERROR:"
         print stderr

 t = Subprocess( print_res, timeout=30, args=[ "cat", "/etc/passwd" ] )
 t.start()

 #start tornado 
 t.ioloop.start()

Tornado WebServices

Quote from site :  

  This is an implementation of SOAP Web Service API, to be used in a Tornado Web Server, taking  advantage of the great features of that server.

https://github.com/rancavil/tornado-webservices

Example : MathService.py
#!/usr/bin/env python
#
# Copyright 2011 Rodrigo Ancavil del Pino
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

import tornado.httpserver
import tornado.ioloop
from tornadows import soaphandler
from tornadows import webservices
from tornadows import xmltypes
from tornadows.soaphandler import webservice

class MathService(soaphandler.SoapHandler):
 """ Service that provides math operations of two float numbers """
 @webservice(_params=[float,float],_returns=float)
 def add(self, a, b):
  result = a+b
  return result
 @webservice(_params=[float,float],_returns=float)
 def sub(self, a, b):
  result = a-b
  return result
 @webservice(_params=[float,float],_returns=float)
 def mult(self, a, b):
  result = a*b
  return result
 @webservice(_params=[float,float],_returns=float)
 def div(self, a, b):
  result = a/b
  return result

if __name__ == '__main__':
   service = [('MathService',MathService)]
   app = webservices.WebService(service)
   ws  = tornado.httpserver.HTTPServer(app)
   ws.listen(8080)
   tornado.ioloop.IOLoop.instance().start()

An example of Auth using Tornado + Motor

Tips from by Yutong Zhao on Google group
 https://groups.google.com/forum/#!topic/python-tornado/l8slxrWHq-c
import tornado.testing
import tornado.web
import tornado.httpserver
import motor
import functools
import pymongo


def authenticate(method):
    @tornado.gen.coroutine
    @functools.wraps(method)
    def wrapper(self, *args, **kwargs):
        key = self.request.headers['Authorization']
        query = {'token': key}
        cursor = self.application.db.users.admins
        result = yield cursor.find_one(query, {'_id': 1})
        if not result:
            self.set_status(401)
            return self.write('Unauthorized')
        else:
            return method(self, *args, **kwargs)
    return wrapper


class GetCurrentUser(tornado.web.RequestHandler):
    @tornado.gen.coroutine
    def get(self):
        current_user = yield self.get_current_user()
        print('Current user:', current_user)

    @tornado.gen.coroutine
    def get_current_user(self):
        key = self.request.headers['Authorization']
        query = {'token': key}
        cursor = self.application.db.users.admins
        result = yield cursor.find_one(query, {'_id': 1})
        if result:
            return result['_id']
        else:
            return None


class GetDecoratedUser(tornado.web.RequestHandler):
    @authenticate
    def get(self):
        print('Success')


class TestCase(tornado.testing.AsyncHTTPTestCase):
    def setUp(self):
        db = pymongo.MongoClient()
        db.users.admins.insert({'_id': 'bob', 'token': '12345'})
        super(TestCase, self).setUp()

    def tearDown(self):
        db = pymongo.MongoClient()
        db.users.admins.remove({})
        super(TestCase, self).tearDown()

    def get_app(self):
        app = tornado.web.Application([
            (r'/current_user', GetCurrentUser),
            (r'/decorator', GetDecoratedUser)
        ])
        app.db = motor.MotorClient()
        return app

    def test_get_current_user(self):
        self.fetch('/current_user', headers={'Authorization': '12345'})
        self.fetch('/current_user', headers={'Authorization': '54321'})

    def test_decorator(self):
        self.fetch('/decorator', headers={'Authorization': '12345'})
        self.fetch('/decorator', headers={'Authorization': '54321'})

Thursday, May 22, 2014

Autoreload process on directory change

Very useful script by By: Steve Krenzel autoreload

Method-based URL dispatcher for the Tornado web server

Nice tip which allows to write methods instead of class for requestHandler

Method-based URL dispatcher for the Tornado web server

Here is the classic way
class Foo(tornado.web.RequestHandler): 
    def get(self): 
        self.write('foo') 
 
class Bar(tornado.web.RequestHandler): 
    def get(self): 
        self.write('bar') 
 
class SimonSays(tornado.web.RequestHandler): 
    def get(self): 
        say = self.get_argument("say") 
        self.write('Simon says, %s' % `say`) 
 
application = tornado.web.Application([ 
    (r"/foo", Foo), 
    (r"/bar", Bar), 
    (r"/simonsays", SimonSays), 
])

The MethodDispatcher way
class FooBar(MethodDispatcher): 
    def foo(self): 
        self.write("foo") 
 
    def bar(self): 
        self.write("bar") 
 
    def simonsays(self, say): 
        self.write("Simon Says, %s" % `say`) 
 
application = tornado.web.Application([ 
    (r"/.*", FooBar) 
])


Highlighted with : http://tohtml.com/python/

webservice-notifications

Quote from site :
A server to plug into your own system, to create a notification system between some server side event, and your users. This aims to provide something close to what you can found on facebook (for example), with a small footprint on your existing architecture and client.
webservice-notification

Tornado 3.3 Documentation

The minimum to  read  Tornado 3.3 Doc