Last active
December 29, 2015 14:49
-
-
Save joerussbowman/7686878 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# | |
# Copyright 2013 Joseph Bowman | |
# | |
# 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 motor | |
import functools | |
import uuid | |
import datetime | |
import settings | |
import time | |
import logging | |
import bson | |
from tornado.web import RequestHandler | |
class MoSession(object): [174/207] | |
""" | |
MoSession class, used to manage persistence across multiple requests. Uses | |
a mongodb backend and Cookies. This library is designed for use with | |
Tornado. | |
Built on top of Motor. | |
The decorator is written to be completely asynchronous and not block. | |
The session is added as session property to your request handler, ie: | |
self.session. It can be manipulated as your would any dictionary object. | |
Included with the library is a settings file, configured for default | |
permissions. Some of the more advanced tuning you can do is with token | |
expiration. In order to create some additional security for sessions used | |
in a non-ssl environment, the token stored in the browser rotates. If you | |
are using ssl, or more interested in performance than security you can set | |
SESSION_TOKEN_TTL to an extremely high number to avoid writes. | |
Note: In an effort increate performance, all data writes are delayed until | |
after the request method has completed. However, security token updates | |
are saved as they happen. | |
""" | |
def __init__(self, req_obj, | |
cookie_path=settings.session["DEFAULT_COOKIE_PATH"], | |
cookie_name=settings.session["COOKIE_NAME"], | |
set_cookie_expires=settings.session["SET_COOKIE_EXPIRES"], | |
session_token_ttl=settings.session["SESSION_TOKEN_TTL"], | |
session_expire_time=settings.session["SESSION_EXPIRE_TIME"], | |
mongo_collection=settings.session["MONGO_COLLECTION"], | |
db=None, | |
callback=None): [142/207] | |
""" | |
__init__ loads the session, checking the browser for a valid session | |
token. It will either validate the session and/or create a new one | |
if necessary. | |
The db object should be a mongodb database, not collection. The | |
collection value is set by the settings for the library. See | |
settings.py for more information. | |
If you already have a db attribute on the request or application | |
objects then there is no need to pass it. Sessions will automatically | |
check those objects for a valid database object to use. | |
""" | |
logging.error("starting init") | |
self.req_obj = req_obj | |
self.cookie_path = cookie_path | |
self.cookie_name = cookie_name | |
self.session_token_ttl = session_token_ttl | |
self.session_expire_time = session_expire_time | |
self.callback = callback | |
if db: | |
self.db = db[mongo_collection] | |
elif hasattr(self.req_obj, "db"): | |
self.db = self.req_obj.db[mongo_collection] | |
elif hasattr(self.req_obj.application, "db"): | |
self.db = self.req_obj.application.db[mongo_collection] | |
else: | |
raise ValueError("Invalid value for db") | |
self.new_session = True | |
self.do_put = False | |
self.do_save = False [110/207] | |
self.do_delete = False | |
self.cookie = self.req_obj.get_secure_cookie(cookie_name) | |
if self.cookie: | |
logging.error("got cookie %s" % self.cookie) | |
(self.token, _id) = self.cookie.split("@") | |
logging.error("looking up session") | |
self.session = self.db.find_one({"_id": | |
bson.ObjectId(_id)}, callback=self._validate_cookie) | |
else: | |
logging.error("no cookie") | |
self._new_session() | |
def _new_session(self): | |
logging.error("starting new session") | |
self.session = {"_id": bson.ObjectId(), | |
"tokens": [str(uuid.uuid4())], | |
"last_token_update": datetime.datetime.utcnow(), | |
"data": {}, | |
} | |
self._put() | |
def _validate_cookie(self, response, error): | |
logging.error("validating cookie") | |
if response: | |
self.session = response | |
if self.token in self.session["tokens"]: | |
self.new_session = False | |
if self.new_session: | |
self._new_session() | |
else: | |
duration = datetime.timedelta(seconds=self.session_token_ttl) [78/207] | |
session_age_limit = datetime.datetime.utcnow() - duration | |
if self.session['last_token_update'] < session_age_limit: | |
self.token = str(uuid.uuid4()) | |
if len(self.session['tokens']) > 2: | |
self.session['tokens'].pop(0) | |
self.session['tokens'].insert(0,self.token) | |
self.session["last_token_update"] = datetime.datetime.utcnow() | |
self.do_put = True | |
if self.do_put: | |
self._put() | |
else: | |
self._handle_response() | |
def _put(self): | |
logging.error("storing id") | |
if self.session.get("_id"): | |
self.db.update({"_id": self.session["_id"]}, {"$set": {"data": | |
self.session["data"], "tokens": self.session["tokens"], | |
"last_token_update": self.session["last_token_update"]}}, | |
upsert=True, | |
callback=self._handle_response) | |
else: | |
self.db.save(self.session, callback=self._handle_response) | |
def _handle_response(self, *args, **kwargs): | |
logging.error("setting cookie and running request handler") | |
cookie = "%s@%s" % (self.session["tokens"][0], self.session["_id"]) | |
self.req_obj.set_secure_cookie(name = self.cookie_name, value = | |
cookie, path = self.cookie_path) | |
self.callback(self.req_obj) | |
[46/207] | |
def get_token(self): | |
return self.cookie | |
def get_id(self): | |
return self.session.get("_id") | |
def delete(self): | |
self.session['tokens'] = [] | |
self.do_delete = True | |
return True | |
def has_key(self, keyname): | |
return self.__contains__(keyname) | |
def get(self, key, default=None): | |
if self.has_key(key): | |
return self[key] | |
else: | |
return default | |
def __delitem__(self, key): | |
del self.session["data"][key] | |
self.do_save = True | |
return True | |
def __getitem__(self, key): | |
return self.session["data"][key] | |
def __setitem__(self, key, val): | |
self.session["data"][key] = val | |
self.do_save = True [14/207] | |
return True | |
def __len__(self): | |
return len(self.session["data"]) | |
def __contains__(self, key): | |
return self.session["data"].has_key(key) | |
def __iter__(self): | |
for key in self.session["data"]: | |
yield key | |
def __str__(self): | |
return u"{%s}" % ', '.join(['"%s" = "%s"' % (k, self.session["data"][k]) for k in self.session["data"]]) | |
def _pass(self, response, error): | |
pass | |
def mosession(method): | |
@functools.wraps(method) | |
def wrapper(self, *args, **kwargs): | |
def on_finish(self, *args, **kwargs): | |
""" | |
This is a monkey patch finish which will save or delete | |
session data at the end of a request. | |
""" | |
super(self.__class__, self).on_finish(*args, **kwargs) | |
logging.error("doing finish") | |
if self.session.do_save: | |
db.update({"_id": self.session.session["_id"]}, {"$set": {"data": | |
self.session.session["data"]}}, | |
callback=self.session._pass) | |
if self.session.do_delete: | |
self.session.db.remove({"_id": self.session.session["_id"]}, | |
callback=self.session._pass) | |
logging.error("starting") | |
self.on_finish = functools.partial(on_finish, self) | |
self.session = MoSession(self, callback=method) | |
#method(self, *args, **kwargs) | |
return wrapper |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class DealerCreatedHandler(BaseHandler): | |
@mosession | |
def get(self): | |
logging.error("I am getting run") | |
confirm = self.get_argument("confirm") | |
account = yield motor.Op( | |
db.accounts.find_one, {"confirm": confirm} | |
) | |
if not account: | |
self.render("error.html", error="Could not find account for that code.") | |
else: | |
self.session["account"] = account["_id"] | |
self.render("dealerCreated.html") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# | |
# Copyright 2009 unscatter.com | |
# | |
# This source code is proprietary and owned by jbowman and may not | |
# be copied, distributed, or run without prior permission from the owner. | |
__author__="bowman.joseph@gmail.com" | |
__date__ ="$September 24, 2011 1:50:35 PM$" | |
session = { | |
"COOKIE_NAME": "mosession", | |
"DEFAULT_COOKIE_PATH": "/", | |
"SESSION_EXPIRE_TIME": 7200, # sessions are valid for 7200 seconds | |
# (2 hours) | |
"SET_COOKIE_EXPIRES": True, # Set to True to add expiration field to | |
# cookie | |
"SESSION_TOKEN_TTL": 5, # Number of seconds a session token is valid | |
# for. | |
"UPDATE_LAST_ACTIVITY": 60, # Number of seconds that may pass before | |
# last_activity is updated | |
"MONGO_COLLECTION": 'mosessions', | |
"MONGO_COLLECTION_SIZE": 100000, | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I updated it, got rid of the gen.coroutine stuff and used callbacks. Was getting a blank page so added a bunch of debugging. The first time I use motor to connect to mongodb it releases everything and runs the request handler. So the set up stuff never happens.
Request looks like
[E 131129 15:56:06 init:224] starting
[E 131129 15:56:06 init:73] starting init
[E 131129 15:56:06 init:96] got cookie b72a364b-b4f7-466f-9ceb-5e6afa727d9e@529685870b49a55ff1272ebf
[E 131129 15:56:06 init:98] looking up session
[I 131129 15:56:06 web:1635] 304 GET /dealer/created/?confirm=8bdbabc5eb7e4a6b87b3281d1e62a3f0 (76.114.245.105) 2.42ms
[E 131129 15:56:06 init:215] doing finish
[E 131129 15:56:06 init:115] validating cookie
[E 131129 15:56:06 init:106] starting new session
[E 131129 15:56:06 init:139] storing id
[E 131129 15:56:06 init:150] setting cookie and running request handler
I need a way to make sure the callback in the session object calls the request object after that's done. I'm not sure why it's skipping ahead.