Anonymous avatar Anonymous committed 2fac117

add session support

Comments (0)

Files changed (3)

friendpaste/application.py

 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import time
 from couchdb import Server, ServerError
-
-from jinja2 import Environment
-from jinja2.loaders import FileSystemLoader
-
-from werkzeug import ClosingIterator, SharedDataMiddleware, redirect, cookie_date
-
-from werkzeug.exceptions import HTTPException, BadRequest, Forbidden, \
-     NotFound
+from werkzeug import Request, SharedDataMiddleware, ClosingIterator, redirect
+from werkzeug.exceptions import HTTPException, NotFound
 
 
 from friendpaste import settings
 from friendpaste.urls import urls_map, all_views
 from friendpaste.utils import local, local_manager
-from friendpaste.http import FPRequest
+from friendpaste.http import FPRequest, session_store
 
 
 class FriendpasteApp(object):
             response.status_code = 404
         except HTTPException, e:
             response = e.get_response(environ)
+            
+            
+        if request.session.should_save:
+            max_age = settings.SESSION_COOKIE_AGE
+            expires = time.time() + settings.SESSION_COOKIE_AGE
+            session_store.save(request.session)
+            response.set_cookie(
+                settings.SESSION_COOKIE_NAME, 
+                request.session.sid, 
+                expires=expires, max_age=max_age,
+                path=settings.SESSION_COOKIE_PATH, 
+                domain=settings.SESSION_COOKIE_DOMAIN, 
+                secure=settings.SESSION_COOKIE_SECURE
+            )
 
         return response(environ, start_response)
 

friendpaste/http.py

 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+from datetime import datetime, timedelta
+import base64
+import md5
+import random
+import cPickle as pickle
+
+from couchdb.client import ResourceNotFound
 import simplejson as json
 from werkzeug import Request, Response
+from werkzeug.contrib.sessions import Session,SessionStore
 
+from friendpaste import settings
+from friendpaste.utils import local, datetime_tojson
+
+
+__all__ = ['DatabaseSessionStore', 'FPRequest', 'FPResponse', 'session_store']
+
+def _encode_session_data(session_data):
+    """ encode session object using pickle and base64.
+    We also sign them with md5 and SECRET_KEY defined
+    in Amisphere settings """
+    pickled = pickle.dumps(session_data)
+    pickled_md5 = md5.new(pickled + settings.SECRET_KEY).hexdigest()
+    return base64.encodestring(pickled + pickled_md5)
+
+def _decode_session_data(session_data):
+    """ decode session object from database """
+    encoded_data = base64.decodestring(session_data)
+    pickled, tamper_check = encoded_data[:-32], encoded_data[-32:]
+    if md5.new(pickled + settings.SECRET_KEY).hexdigest() != tamper_check:
+        raise SuspiciousOperation, "User tampered with session cookie."
+    try:
+        return pickle.loads(pickled)
+    # Unpickling can cause a variety of exceptions. If something happens,
+    # just return an empty dictionary (an empty session).
+    except:
+        return {}
+
+class DatabaseSessionStore(SessionStore):
+    """ database session store """
+
+    def __init__(self, session_class=Session):
+        SessionStore.__init__(self, session_class)
+
+    def save(self, session):
+        """ save session in couchdb database """
+        expire = datetime.now() + timedelta(seconds=settings.SESSION_COOKIE_AGE)
+        try:
+            local.db["session/%s" % session.sid] = {
+                    'session_key':session.sid, 
+                    'session_data': _encode_session_data(dict(session)),
+                    'expire_date': datetime_tojson(expire)            
+            }
+        except:
+            s = local.db["session/%s" % session.sid]
+            s['session_data'] = _encode_session_data(dict(session))
+            s['expire_date'] = datetime_tojson(expire)
+            local.db['session/%s' % session.sid] = s
+       
+    def delete(self, session):
+        """ delete session """
+        try:
+            del local.db['session/%s' % session.sid]
+        except AttributeError:
+            pass
+       
+    def get(self, sid):
+        """ get session associated to sid. sid
+        is retrieved from a secure cookie. Secure cookie name
+        is defined in Amisphere settings. """
+        s = None
+        try:
+            s = local.db['session/%s' % sid]
+        except ResourceNotFound:
+            pass
+            
+        if s is None or not self.is_valid_key(sid):
+            return self.new()
+
+        return self.session_class(_decode_session_data(s['session_data']), sid, False)
+session_store = DatabaseSessionStore()
 
 class FPRequest(Request):
     charset = 'utf-8'
     def __init__(self, app, environ):
         super(FPRequest, self).__init__(environ)
         self.app = app
+        self.sid = self.cookies.get(settings.SESSION_COOKIE_NAME)
+        if not self.sid:
+            self.session = session_store.new()
+        else:
+            self.session = session_store.get(self.sid)
 
 class FPResponse(Response):
     """

friendpaste/settings.py

         ('bash', 'Bash')
 ]
     
+############
+# SESSIONS #
+############
+SESSION_COOKIE_NAME = 'FRIENDPASTE_SID'
+SESSION_COOKIE_AGE = 60 * 60 * 24 * 7 * 2
+SESSION_COOKIE_DOMAIN = None
+SESSION_COOKIE_PATH = '/'
+SESSION_COOKIE_SECURE = False
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.