Lazy loading of KVSessionExtension

- call cleanup_sessions on first kvstore access
  this allows to run cmdline actions without redis (and makes it faster)
- Allow development using DictStore by setting REDIS_ADDRESS to the empty string in env
- don't sign 64bit random session id as suggested by nextgens
master
Alexander Graf 4 years ago
parent 1fd7a9c578
commit 83b1fbb9d6

@ -1,8 +1,8 @@
""" Mailu admin app
"""
import flask import flask
import flask_bootstrap import flask_bootstrap
import redis
from flask_kvsession import KVSessionExtension
from simplekv.memory.redisstore import RedisStore
from mailu import utils, debug, models, manage, configuration from mailu import utils, debug, models, manage, configuration
@ -20,7 +20,8 @@ def create_app_from_config(config):
# Initialize application extensions # Initialize application extensions
config.init_app(app) config.init_app(app)
models.db.init_app(app) models.db.init_app(app)
KVSessionExtension(RedisStore(redis.StrictRedis().from_url('redis://{0}/3'.format(config['REDIS_ADDRESS']))), app).cleanup_sessions(app) utils.kvsession.init_kvstore(config)
utils.kvsession.init_app(app)
utils.limiter.init_app(app) utils.limiter.init_app(app)
utils.babel.init_app(app) utils.babel.init_app(app)
utils.login.init_app(app) utils.login.init_app(app)
@ -53,8 +54,7 @@ def create_app_from_config(config):
def create_app(): def create_app():
""" Create a new application based on the config module """ Create a new application based on the config module
""" """
config = configuration.ConfigManager() config = configuration.ConfigManager()
return create_app_from_config(config) return create_app_from_config(config)

@ -1,11 +1,18 @@
from mailu import models, limiter """ Mailu admin app utilities
"""
from mailu import limiter
import flask import flask
import flask_login import flask_login
import flask_script
import flask_migrate import flask_migrate
import flask_babel import flask_babel
import flask_kvsession
import redis
from simplekv.memory import DictStore
from simplekv.memory.redisstore import RedisStore
from itsdangerous.encoding import want_bytes
from werkzeug.contrib import fixers from werkzeug.contrib import fixers
@ -33,6 +40,10 @@ def get_locale():
# Proxy fixer # Proxy fixer
class PrefixMiddleware(object): class PrefixMiddleware(object):
""" fix proxy headers """
def __init__(self):
self.app = None
def __call__(self, environ, start_response): def __call__(self, environ, start_response):
prefix = environ.get('HTTP_X_FORWARDED_PREFIX', '') prefix = environ.get('HTTP_X_FORWARDED_PREFIX', '')
if prefix: if prefix:
@ -48,3 +59,53 @@ proxy = PrefixMiddleware()
# Data migrate # Data migrate
migrate = flask_migrate.Migrate() migrate = flask_migrate.Migrate()
# session store
class NullSigner(object):
"""NullSigner does not sign nor unsign"""
def __init__(self, *args, **kwargs):
pass
def sign(self, value):
"""Signs the given string."""
return want_bytes(value)
def unsign(self, signed_value):
"""Unsigns the given string."""
return want_bytes(signed_value)
class KVSessionIntf(flask_kvsession.KVSessionInterface):
""" KVSession interface allowing to run int function on first access """
def __init__(self, app, init_fn=None):
if init_fn:
app.kvsession_init = init_fn
else:
self._first_run(None)
def _first_run(self, app):
if app:
app.kvsession_init()
self.open_session = super().open_session
self.save_session = super().save_session
def open_session(self, app, request):
self._first_run(app)
return super().open_session(app, request)
def save_session(self, app, session, response):
self._first_run(app)
return super().save_session(app, session, response)
class KVSessionExt(flask_kvsession.KVSessionExtension):
""" Activates Flask-KVSession for an application. """
def init_kvstore(self, config):
""" Initialize kvstore - fallback to DictStore without REDIS_ADDRESS """
if addr := config.get('REDIS_ADDRESS'):
self.default_kvstore = RedisStore(redis.StrictRedis().from_url(f'redis://{addr}/3'))
else:
self.default_kvstore = DictStore()
def init_app(self, app, session_kvstore=None):
""" Initialize application and KVSession. """
super().init_app(app, session_kvstore)
app.session_interface = KVSessionIntf(app, self.cleanup_sessions)
kvsession = KVSessionExt()
flask_kvsession.Signer = NullSigner

@ -39,6 +39,7 @@ python-editor==1.0.4
pytz==2019.1 pytz==2019.1
PyYAML==5.1 PyYAML==5.1
redis==3.2.1 redis==3.2.1
simplekv==0.14.1
#alpine3:12 provides six==1.15.0 #alpine3:12 provides six==1.15.0
#six==1.12.0 #six==1.12.0
socrate==0.1.1 socrate==0.1.1

Loading…
Cancel
Save