Skip to content
This repository has been archived by the owner on Mar 24, 2021. It is now read-only.

Commit

Permalink
Re-add Redis session
Browse files Browse the repository at this point in the history
Cookies have a size limit of 4096 so reintroducing the Redis session to
allow larger dashboards to be edited.
  • Loading branch information
robyoung committed Oct 9, 2014
1 parent 8215990 commit 40f03cc
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 0 deletions.
8 changes: 8 additions & 0 deletions admin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
from flask_wtf.csrf import CsrfProtect
from os import getenv, path
from raven.contrib.flask import Sentry
from redis import Redis

from admin.core import log_handler
from admin.redis_session import RedisSessionInterface

app = Flask(__name__)

Expand All @@ -13,6 +15,12 @@
app.config['LOG_LEVEL'] = "INFO"
app.config.from_object('admin.config.{0}'.format(GOVUK_ENV))
app.secret_key = app.config['COOKIE_SECRET_KEY']
app.redis_instance = Redis(
host=app.config['REDIS_HOST'],
port=app.config['REDIS_PORT'],
)
app.session_interface = RedisSessionInterface(
redis=app.redis_instance, prefix='admin_app:session:')

# adds uncaught exception handlers to app and submits to sentry
# this will only send when SENTRY_DSN is defined in config
Expand Down
3 changes: 3 additions & 0 deletions admin/config/development.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@

DEBUG = True

REDIS_HOST = 'localhost'
REDIS_PORT = 6379

FAKE_OAUTH_TOKEN = 'development-oauth-access-token'
FAKE_OAUTH_USER = {
"email": "some.user@digital.cabinet-office.gov.uk",
Expand Down
103 changes: 103 additions & 0 deletions admin/redis_session.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import pickle
from datetime import timedelta
from uuid import uuid4
from redis import Redis
from werkzeug.datastructures import CallbackDict
from flask.sessions import SessionInterface, SessionMixin
import logging
# see http://flask.pocoo.org/snippets/75/


class RedisSession(CallbackDict, SessionMixin):

def __init__(
self, redis=None, initial=None, sid=None, new=False, prefix=''):
def on_update(self):
self.modified = True
CallbackDict.__init__(self, initial, on_update)
self.sid = sid
self.new = new
self.modified = False
self.redis = redis
self.logger = logging.getLogger(__name__)
self.prefix = prefix

def store_session_for_user(self, uid):
""" Associate the current session with the given uid
Used for later destroying those sessions when reauthing with signon
uid - a signon user_id
"""
self.logger.debug(
'saving session id {} for user {} to redis'.format(self.sid, uid)
)
self.redis.lpush(
'{}user_sessions:{}'.format(self.prefix, uid),
self.sid,
)

def delete_sessions_for_user(self, uid):
""" Delete all sessions for a user with given uid
uid - a signon user_id
"""
self.logger.debug('deleting sessions for {}'.format(uid))
session_ids = self.redis.lrange(
'{}user_sessions:{}'.format(self.prefix, uid),
0,
1
)
for sid in session_ids:
self.logger.debug('signing out {}'.format(sid))
self.redis.delete('{}{}'.format(self.prefix, sid))


class RedisSessionInterface(SessionInterface):
serializer = pickle
session_class = RedisSession

def __init__(self, redis=None, prefix=''):
if redis is None:
redis = Redis()
self.redis = redis
self.prefix = prefix + 'session:'

def generate_sid(self):
return str(uuid4())

def get_redis_expiration_time(self, app, session):
if session.permanent:
return app.permanent_session_lifetime
# set to 2 hours to match signon expiry time
return timedelta(hours=2)

def open_session(self, app, request):
sid = request.cookies.get(app.session_cookie_name)
if not sid:
sid = self.generate_sid()
return self.session_class(
redis=self.redis, sid=sid, new=True, prefix=self.prefix)
val = self.redis.get(self.prefix + sid)
if val is not None:
data = self.serializer.loads(val)
return self.session_class(
redis=self.redis, initial=data, sid=sid, prefix=self.prefix)
return self.session_class(
redis=self.redis, sid=sid, new=True, prefix=self.prefix)

def save_session(self, app, session, response):
domain = self.get_cookie_domain(app)
if not session:
self.redis.delete(self.prefix + session.sid)
if session.modified:
response.delete_cookie(app.session_cookie_name,
domain=domain)
return
redis_exp = self.get_redis_expiration_time(app, session)
cookie_exp = self.get_expiration_time(app, session)
val = self.serializer.dumps(dict(session))
self.redis.setex(self.prefix + session.sid, val,
int(redis_exp.total_seconds()))
response.set_cookie(app.session_cookie_name, session.sid,
expires=cookie_exp, httponly=True,
domain=domain)

0 comments on commit 40f03cc

Please sign in to comment.