From b9f7fa1f6f97307222483c1266d9ef7da03b0ab2 Mon Sep 17 00:00:00 2001 From: Leo Hemsted Date: Fri, 5 Jan 2018 16:27:51 +0000 Subject: [PATCH] Replace cookie implementation with flask builtin secure cookie. However, by default, session cookies don't expire (only cleared out by the end user's browser). This is dumb. You'd think, given that there's `config['PERMANENT_SESSION_LIFETIME']`, that you'd enable permanent sessions in the config too - but no, you have to declare it for each request. session.permanent is also, helpfully, a way of saying that the session isn't permanent - in that, it will expire on its own, as opposed to being controlled by the browser's session. Because session is a proxy, it's only accessible from within a request context, so we need to set this before every request :roll_eyes: http://flask.pocoo.org/docs/0.12/api/#flask.session https://stackoverflow.com/questions/34118093/flask-permanent-session-where-to-define-them --- app/__init__.py | 13 +++++++-- app/its_dangerous_session.py | 53 ------------------------------------ 2 files changed, 10 insertions(+), 56 deletions(-) delete mode 100644 app/its_dangerous_session.py diff --git a/app/__init__.py b/app/__init__.py index 7669fbc73..48a664431 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -38,7 +38,6 @@ from werkzeug.local import LocalProxy from app import proxy_fix from app.config import configs from app.asset_fingerprinter import AssetFingerprinter -from app.its_dangerous_session import ItsdangerousSessionInterface from app.notify_client.service_api_client import ServiceAPIClient from app.notify_client.api_key_api_client import ApiKeyApiClient from app.notify_client.invite_api_client import InviteApiClient @@ -124,8 +123,6 @@ def create_app(application): proxy_fix.init_app(application) - application.session_interface = ItsdangerousSessionInterface() - add_template_filters(application) register_errorhandlers(application) @@ -139,6 +136,16 @@ def init_app(application): application.before_request(load_service_before_request) application.before_request(request_helper.check_proxy_header_before_request) + @application.before_request + def make_session_permanent(): + # this is dumb. You'd think, given that there's `config['PERMANENT_SESSION_LIFETIME']`, that you'd enable + # permanent sessions in the config too - but no, you have to declare it for each request. + # https://stackoverflow.com/questions/34118093/flask-permanent-session-where-to-define-them + # session.permanent is also, helpfully, a way of saying that the session isn't permanent - in that, it will + # expire on its own, as opposed to being controlled by the browser's session. Because session is a proxy, it's + # only accessible from within a request context, so we need to set this before every request :rolls_eyes: + session.permanent = True + @application.context_processor def _attach_current_service(): return {'current_service': current_service} diff --git a/app/its_dangerous_session.py b/app/its_dangerous_session.py deleted file mode 100644 index e16cecc55..000000000 --- a/app/its_dangerous_session.py +++ /dev/null @@ -1,53 +0,0 @@ -from datetime import timedelta, datetime - -from werkzeug.datastructures import CallbackDict -from flask.sessions import SessionInterface, SessionMixin -from itsdangerous import URLSafeTimedSerializer, BadSignature - - -class ItsdangerousSession(CallbackDict, SessionMixin): - def __init__(self, initial=None): - def on_update(self): - self.modified = True - - CallbackDict.__init__(self, initial, on_update) - self.modified = False - - -class ItsdangerousSessionInterface(SessionInterface): - session_class = ItsdangerousSession - - def get_serializer(self, app): - salt = app.config.get('DANGEROUS_SALT') - if not app.secret_key: - return None - return URLSafeTimedSerializer(app.secret_key, - salt=salt) - - def open_session(self, app, request): - s = self.get_serializer(app) - if s is None: - return None - val = request.cookies.get(app.session_cookie_name) - if not val: - return self.session_class() - max_age = app.permanent_session_lifetime.total_seconds() - try: - data = s.loads(val, max_age=max_age) - return self.session_class(data) - except BadSignature: - return self.session_class() - - def save_session(self, app, session, response): - domain = self.get_cookie_domain(app) - if not session: - if session.modified: - response.delete_cookie(app.session_cookie_name, - domain=domain) - return - session.permanent = True - expires = datetime.utcnow() + timedelta(seconds=app.config.get('PERMANENT_SESSION_LIFETIME')) - val = self.get_serializer(app).dumps(dict(session)) - response.set_cookie(app.session_cookie_name, val, - expires=expires, httponly=True, - domain=domain, secure=app.config.get('SESSION_COOKIE_SECURE'))