mirror of
https://github.com/Mailu/Mailu.git
synced 2024-12-12 10:45:38 +02:00
Tweak sessions
simplify: - make all sessions permanent by default - update the TTL of sessions on access (save always) - fix session-expiry, modulo 8byte precision
This commit is contained in:
parent
ea96a68eb4
commit
02c93c44f2
@ -70,7 +70,8 @@ DEFAULT_CONFIG = {
|
||||
# Advanced settings
|
||||
'LOG_LEVEL': 'WARNING',
|
||||
'SESSION_KEY_BITS': 128,
|
||||
'SESSION_LIFETIME': 24,
|
||||
'SESSION_TIMEOUT': 3600,
|
||||
'PERMANENT_SESSION_LIFETIME': 30*24*3600,
|
||||
'SESSION_COOKIE_SECURE': True,
|
||||
'CREDENTIAL_ROUNDS': 12,
|
||||
'TZ': 'Etc/UTC',
|
||||
@ -152,7 +153,7 @@ class ConfigManager:
|
||||
self.config['SESSION_STORAGE_URL'] = f'redis://{self.config["REDIS_ADDRESS"]}/3'
|
||||
self.config['SESSION_COOKIE_SAMESITE'] = 'Strict'
|
||||
self.config['SESSION_COOKIE_HTTPONLY'] = True
|
||||
self.config['PERMANENT_SESSION_LIFETIME'] = timedelta(hours=int(self.config['SESSION_LIFETIME']))
|
||||
self.config['SESSION_PERMANENT'] = True
|
||||
hostnames = [host.strip() for host in self.config['HOSTNAMES'].split(',')]
|
||||
self.config['AUTH_RATELIMIT_EXEMPTION'] = set(ipaddress.ip_network(cidr, False) for cidr in (cidr.strip() for cidr in self.config['AUTH_RATELIMIT_EXEMPTION'].split(',')) if cidr)
|
||||
self.config['MESSAGE_RATELIMIT_EXEMPTION'] = set([s for s in self.config['MESSAGE_RATELIMIT_EXEMPTION'].lower().replace(' ', '').split(',') if s])
|
||||
|
@ -28,6 +28,7 @@ import flask_babel
|
||||
import ipaddress
|
||||
import redis
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from flask.sessions import SessionMixin, SessionInterface
|
||||
from itsdangerous.encoding import want_bytes
|
||||
from werkzeug.datastructures import CallbackDict
|
||||
@ -125,8 +126,6 @@ migrate = flask_migrate.Migrate()
|
||||
class RedisStore:
|
||||
""" Stores session data in a redis db. """
|
||||
|
||||
has_ttl = True
|
||||
|
||||
def __init__(self, redisstore):
|
||||
self.redis = redisstore
|
||||
|
||||
@ -157,8 +156,6 @@ class RedisStore:
|
||||
class DictStore:
|
||||
""" Stores session data in a python dict. """
|
||||
|
||||
has_ttl = False
|
||||
|
||||
def __init__(self):
|
||||
self.dict = {}
|
||||
|
||||
@ -166,7 +163,7 @@ class DictStore:
|
||||
""" load item from store. """
|
||||
return self.dict[key]
|
||||
|
||||
def put(self, key, value, ttl_secs=None):
|
||||
def put(self, key, value, ttl=None):
|
||||
""" save item to store. """
|
||||
self.dict[key] = value
|
||||
|
||||
@ -284,14 +281,11 @@ class MailuSession(CallbackDict, SessionMixin):
|
||||
if key != self._key:
|
||||
self.delete()
|
||||
|
||||
# remember time to refresh
|
||||
self['_refresh'] = int(time.time()) + self.app.permanent_session_lifetime.total_seconds()/2
|
||||
|
||||
# save session
|
||||
self.app.session_store.put(
|
||||
key,
|
||||
pickle.dumps(dict(self)),
|
||||
self.app.permanent_session_lifetime.total_seconds()
|
||||
int(app.config['SESSION_TIMEOUT']),
|
||||
)
|
||||
|
||||
self._key = key
|
||||
@ -301,11 +295,6 @@ class MailuSession(CallbackDict, SessionMixin):
|
||||
|
||||
return set_cookie
|
||||
|
||||
def needs_refresh(self):
|
||||
""" Checks if server side session needs to be refreshed. """
|
||||
|
||||
return int(time.time()) > self.get('_refresh', 0)
|
||||
|
||||
class MailuSessionConfig:
|
||||
""" Stores sessions crypto config """
|
||||
|
||||
@ -350,7 +339,7 @@ class MailuSessionConfig:
|
||||
""" Generate base64 representation of creation time. """
|
||||
return self._encode(int(now or time.time()).to_bytes(8, byteorder='big').lstrip(b'\0'))
|
||||
|
||||
def parse_key(self, key, app=None, validate=False, now=None):
|
||||
def parse_key(self, key, app=None, now=None):
|
||||
""" Split key into sid, uid and creation time. """
|
||||
|
||||
if not (isinstance(key, bytes) and self._key_min <= len(key) <= self._key_max):
|
||||
@ -365,13 +354,12 @@ class MailuSessionConfig:
|
||||
if created is None or self._decode(uid) is None or self._decode(sid) is None:
|
||||
return None
|
||||
|
||||
# validate creation time when requested or store does not support ttl
|
||||
if validate or not app.session_store.has_ttl:
|
||||
if now is None:
|
||||
now = int(time.time())
|
||||
created = int.from_bytes(created, byteorder='big')
|
||||
if not created < now < created + app.permanent_session_lifetime.total_seconds():
|
||||
return None
|
||||
# validate creation time
|
||||
if now is None:
|
||||
now = int(time.time())
|
||||
created = int.from_bytes(created, byteorder='big')
|
||||
if not created <= now <= created + app.config['PERMANENT_SESSION_LIFETIME']:
|
||||
return None
|
||||
|
||||
return (uid, sid, crt)
|
||||
|
||||
@ -410,23 +398,12 @@ class MailuSessionInterface(SessionInterface):
|
||||
if session.accessed:
|
||||
response.vary.add('Cookie')
|
||||
|
||||
set_cookie = session.permanent and app.config['SESSION_REFRESH_EACH_REQUEST']
|
||||
need_refresh = session.needs_refresh()
|
||||
|
||||
# save modified session or refresh unmodified session
|
||||
if session.modified or need_refresh:
|
||||
set_cookie |= session.save()
|
||||
|
||||
# set cookie on refreshed permanent sessions
|
||||
if need_refresh and session.permanent:
|
||||
set_cookie = True
|
||||
|
||||
# set or update cookie if necessary
|
||||
if set_cookie:
|
||||
# save session and update cookie if necessary
|
||||
if session.save():
|
||||
response.set_cookie(
|
||||
app.session_cookie_name,
|
||||
session.sid,
|
||||
expires=self.get_expiration_time(app, session),
|
||||
expires=datetime.now()+timedelta(seconds=int(app.config['PERMANENT_SESSION_LIFETIME'])),
|
||||
httponly=self.get_cookie_httponly(app),
|
||||
domain=self.get_cookie_domain(app),
|
||||
path=self.get_cookie_path(app),
|
||||
@ -446,7 +423,7 @@ class MailuSessionExtension:
|
||||
|
||||
count = 0
|
||||
for key in app.session_store.list():
|
||||
if not app.session_config.parse_key(key, app, validate=True, now=now):
|
||||
if not app.session_config.parse_key(key, app, now=now):
|
||||
app.session_store.delete(key)
|
||||
count += 1
|
||||
|
||||
|
@ -181,7 +181,7 @@ The ``CREDENTIAL_ROUNDS`` (default: 12) setting is the number of rounds used by
|
||||
|
||||
The ``SESSION_COOKIE_SECURE`` (default: True) setting controls the secure flag on the cookies of the administrative interface. It should only be turned off if you intend to access it over plain HTTP.
|
||||
|
||||
``SESSION_LIFETIME`` (default: 24) is the length in hours a session is valid for on the administrative interface.
|
||||
``SESSION_TIMEOUT`` (default: 3600) is the maximum amount of time in seconds between requests before a session is invalidated. ``PERMANENT_SESSION_LIFETIME`` (default: 108000) is the maximum amount of time in seconds a session can be kept alive for if it hasn't timed-out.
|
||||
|
||||
The ``LOG_LEVEL`` setting is used by the python start-up scripts as a logging threshold.
|
||||
Log messages equal or higher than this priority will be printed.
|
||||
|
Loading…
Reference in New Issue
Block a user