From e6fb72c29550d242c93f07b7f16ea7cdcf2243ba Mon Sep 17 00:00:00 2001 From: Lex Date: Mon, 15 Jan 2024 15:23:11 +1000 Subject: [PATCH] Update documentation and change log --- CHANGES.rst | 11 +- docs/api.rst | 2 +- docs/conf.py | 3 + docs/config.rst | 283 ++++++++++++++++++++++++---------- docs/index.rst | 12 +- docs/installation.rst | 45 ++++++ docs/interfaces.rst | 68 -------- src/flask_session/__init__.py | 2 +- src/flask_session/sessions.py | 61 +++++--- 9 files changed, 301 insertions(+), 186 deletions(-) create mode 100644 docs/installation.rst delete mode 100644 docs/interfaces.rst diff --git a/CHANGES.rst b/CHANGES.rst index fd71d35f..84ee2292 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,7 +1,7 @@ -Version 0.6.0 -------------- +Version 0.6.0-RC1 +------------------ -Released 2023-00-00 +Unreleased - Use ``should_set_cookie`` for preventing each request from saving the session again. - Permanent session otherwise empty will not be saved. @@ -9,8 +9,13 @@ Released 2023-00-00 entropy (was previously 122). - Explicitly name support for python-memcached, pylibmc and pymemcache. - Introduce SESSION_KEY_LENGTH to control the length of the session key in bytes, default is 32. +- Fix pymongo 4.0 compatibility. - Fix expiry is None bug in SQLAlchemy. +- Fix bug when existing SQLAlchemy db instance. +- Support SQLAlchemy SESSION_SQLALCHEMY_SEQUENCE, SESSION_SQLALCHEMY_SCHEMA and SESSION_SQLALCHEMY_BINDKEY - Drop support for Redis < 2.6.12. +- Fix empty sessions being saved. +- Support Flask 3.0 and Werkzeug 3.0 Version 0.5.0 ------------- diff --git a/docs/api.rst b/docs/api.rst index b3cc4c58..45103d85 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -10,7 +10,7 @@ API .. attribute:: sid - Session id, internally we use :func:`uuid.uuid4` to generate one + Session id, internally we use :func:`secrets.token_urlsafe` to generate one session id. You can access it with ``session.sid``. .. autoclass:: NullSessionInterface diff --git a/docs/conf.py b/docs/conf.py index 13effa6d..71d55a27 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -22,4 +22,7 @@ "github_button": True, "github_user": "pallets-eco", "github_repo": "flask-session", + "github_type": "star", + "github_banner": True, + "show_related": True, } diff --git a/docs/config.rst b/docs/config.rst index 2d68bad0..bca1972a 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -1,98 +1,215 @@ Configuration ============= -The following configuration values exist for Flask-Session. Flask-Session -loads these values from your Flask application config, so you should configure -your app first before you pass it to Flask-Session. Note that these values -cannot be modified after the ``init_app`` was applyed so make sure to not -modify them at runtime. +Backend Configuration +--------------------- + +Here is an example of how to configure a redis backend: + +.. code-block:: python + + app.config['SESSION_TYPE'] = 'redis' + app.config['SESSION_REDIS'] = Redis.from_url('redis://127.0.0.1:6379') We are not supplying something like ``SESSION_REDIS_HOST`` and ``SESSION_REDIS_PORT``, if you want to use the ``RedisSessionInterface``, you should configure ``SESSION_REDIS`` to your own ``redis.Redis`` instance. -This gives you more flexibility, like maybe you want to use the same -``redis.Redis`` instance for cache purpose too, then you do not need to keep +This gives you more flexibility, such as using the same +``redis.Redis`` instance for cache purposes too, then you do not need to keep two ``redis.Redis`` instance in the same process. -The following configuration values are builtin configuration values within -Flask itself that are related to session. **They are all understood by -Flask-Session, for example, you should use PERMANENT_SESSION_LIFETIME -to control your session lifetime.** - -================================= ========================================= -``SESSION_COOKIE_NAME`` the name of the session cookie -``SESSION_COOKIE_DOMAIN`` the domain for the session cookie. If - this is not set, the cookie will be - valid for all subdomains of - ``SERVER_NAME``. -``SESSION_COOKIE_PATH`` the path for the session cookie. If - this is not set the cookie will be valid - for all of ``APPLICATION_ROOT`` or if - that is not set for ``'/'``. -``SESSION_COOKIE_HTTPONLY`` controls if the cookie should be set - with the httponly flag. Defaults to - `True`. -``SESSION_COOKIE_SECURE`` controls if the cookie should be set - with the secure flag. Defaults to - `False`. -``PERMANENT_SESSION_LIFETIME`` the lifetime of a permanent session as - :class:`datetime.timedelta` object. - Starting with Flask 0.8 this can also be - an integer representing seconds. -================================= ========================================= - -A list of configuration keys also understood by the extension: - -============================= ============================================== -``SESSION_TYPE`` Specifies which type of session interface to - use. Built-in session types: - - - **null**: NullSessionInterface (default) - - **redis**: RedisSessionInterface - - **memcached**: MemcachedSessionInterface - - **filesystem**: FileSystemSessionInterface - - **mongodb**: MongoDBSessionInterface - - **sqlalchemy**: SqlAlchemySessionInterface -``SESSION_PERMANENT`` Whether use permanent session or not, default - to be ``True`` -``SESSION_USE_SIGNER`` Whether sign the session cookie sid or not, - if set to ``True``, you have to set - :attr:`flask.Flask.secret_key`, default to be - ``False`` -``SESSION_KEY_PREFIX`` A prefix that is added before all session keys. - This makes it possible to use the same backend - storage server for different apps, default - "session:" -``SESSION_REDIS`` A ``redis.Redis`` instance, default connect to - ``127.0.0.1:6379`` -``SESSION_MEMCACHED`` A ``memcache.Client`` instance, default connect - to ``127.0.0.1:11211`` -``SESSION_FILE_DIR`` The directory where session files are stored. - Default to use `flask_session` directory under - current working directory. -``SESSION_FILE_THRESHOLD`` The maximum number of items the session stores - before it starts deleting some, default 500 -``SESSION_FILE_MODE`` The file mode wanted for the session files, - default 0600 -``SESSION_MONGODB`` A ``pymongo.MongoClient`` instance, default - connect to ``127.0.0.1:27017`` -``SESSION_MONGODB_DB`` The MongoDB database you want to use, default - "flask_session" -``SESSION_MONGODB_COLLECT`` The MongoDB collection you want to use, default - "sessions" -``SESSION_SQLALCHEMY`` A ``flask_sqlalchemy.SQLAlchemy`` instance - whose database connection URI is configured - using the ``SQLALCHEMY_DATABASE_URI`` parameter -``SESSION_SQLALCHEMY_TABLE`` The name of the SQL table you want to use, - default "sessions" -============================= ============================================== - -Basically you only need to configure ``SESSION_TYPE``. +If you do not set ``SESSION_REDIS``, Flask-Session will assume you are developing locally and create a +``redis.Redis`` instance for you. It is expected you supply an instance of +``redis.Redis`` in production. .. note:: By default, all non-null sessions in Flask-Session are permanent. -.. versionadded:: 0.2 +Relevant Flask Configuration Values +------------------------------------- +The following configuration values are builtin configuration values within +Flask itself that are relate to the Flask session cookie set on the browser. Flask-Session +loads these values from your Flask application config, so you should configure +your app first before you pass it to Flask-Session. + +Note that these values +cannot be modified after the ``init_app`` was applied so make sure to not +modify them at runtime. + +``PERMANENT_SESSION_LIFETIME`` effects not only the browser cookie lifetime but also +the expiration in the server side session storage. + + +.. py:data:: SESSION_COOKIE_NAME + + The name of the session cookie. + +.. py:data:: SESSION_COOKIE_DOMAIN + + The domain for the session cookie. If this is not set, the cookie will be valid for all subdomains of ``SERVER_NAME``. + +.. py:data:: SESSION_COOKIE_PATH + + The path for the session cookie. If this is not set the cookie will be valid for all of ``APPLICATION_ROOT`` or if that is not set for ``'/'``. + +.. py:data:: SESSION_COOKIE_HTTPONLY + + Controls if the cookie should be set with the httponly flag. + + Default: ``True`` + +.. py:data:: SESSION_COOKIE_SECURE + + Controls if the cookie should be set with the secure flag. Browsers will only send cookies with requests over HTTPS if the cookie is marked "secure". The application must be served over HTTPS for this to make sense. + + Default: ``False`` + +.. py:data:: PERMANENT_SESSION_LIFETIME + + The lifetime of a permanent session as :class:`datetime.timedelta` object. Starting with Flask 0.8 this can also be an integer representing seconds. + + +Flask-Session Configuration Values +---------------------------------- + +.. py:data:: SESSION_TYPE + + Specifies which type of session interface to use. Built-in session types: + + - **null**: NullSessionInterface (default) + - **redis**: RedisSessionInterface + - **memcached**: MemcachedSessionInterface + - **filesystem**: FileSystemSessionInterface + - **mongodb**: MongoDBSessionInterface + - **sqlalchemy**: SqlAlchemySessionInterface + +.. py:data:: SESSION_PERMANENT + + Whether use permanent session or not. + + Default: ``True`` + +.. py:data:: SESSION_USE_SIGNER + + Whether sign the session cookie sid or not, if set to ``True``, you have to set :attr:`flask.Flask.secret_key`. + + Default: ``False`` + +.. py:data:: SESSION_KEY_PREFIX + + A prefix that is added before all session keys. This makes it possible to use the same backend storage server for different apps. + + Default: ``'session:'`` + +.. py:data:: SESSION_ID_LENGTH + + The length of the session identifier in bytes (of entropy). + + Default: ``32`` + +.. versionadded:: 0.6 +``SESSION_ID_LENGTH`` + +Backend-specific Configuration Values +--------------------------------------- + +Redis +~~~~~~~~~~~~~~~~~~~~~~~ + +.. py:data:: SESSION_REDIS + + A ``redis.Redis`` instance. + + Default: Instance connected to ``127.0.0.1:6379`` + + +Memcached +~~~~~~~~~~~~~~~~~~~~~~~ + +.. py:data:: SESSION_MEMCACHED + + A ``memcache.Client`` instance. + + Default: Instance connected to ``127.0.0.1:6379`` + + +FileSystem +~~~~~~~~~~~~~~~~~~~~~~~ + +.. py:data:: SESSION_FILE_DIR + + The directory where session files are stored. + + Default: ``flask_session`` directory under current working directory. + +.. py:data:: SESSION_FILE_THRESHOLD + + The maximum number of items the session stores before it starts deleting some. + + Default: ``500`` + +.. py:data:: SESSION_FILE_MODE + + The file mode wanted for the session files. + + Default: ``0600`` + + +MongoDB +~~~~~~~~~~~~~~~~~~~~~~~ + +.. py:data:: SESSION_MONGODB + + A ``pymongo.MongoClient`` instance. + + Default: Instance connected to ``127.0.0.1:27017`` + +.. py:data:: SESSION_MONGODB_DB + + The MongoDB database you want to use. + + Default: ``'flask_session'`` + +.. py:data:: SESSION_MONGODB_COLLECT + + The MongoDB collection you want to use. + + Default: ``'sessions'`` + + +SqlAlchemy +~~~~~~~~~~~~~~~~~~~~~~~ + +.. py:data:: SESSION_SQLALCHEMY + + A ``flask_sqlalchemy.SQLAlchemy`` instance whose database connection URI is configured using the ``SQLALCHEMY_DATABASE_URI`` parameter. + + Must be set in flask_sqlalchemy version 3.0 or higher. + +.. py:data:: SESSION_SQLALCHEMY_TABLE + + The name of the SQL table you want to use. + + Default: ``'sessions'`` + +.. py:data:: SESSION_SQLALCHEMY_SEQUENCE + + The name of the sequence you want to use for the primary key. + + Default: ``None`` + +.. py:data:: SESSION_SQLALCHEMY_SCHEMA + + The name of the schema you want to use. + + Default: ``None`` + +.. py:data:: SESSION_SQLALCHEMY_BIND_KEY + + The name of the bind key you want to use. + + Default: ``None`` - ``SESSION_TYPE``: **sqlalchemy**, ``SESSION_USE_SIGNER`` +.. versionadded:: 0.6 +``SESSION_SQLALCHEMY_BIND_KEY``, ``SESSION_SQLALCHEMY_SCHEMA``, ``SESSION_SQLALCHEMY_SEQUENCE`` diff --git a/docs/index.rst b/docs/index.rst index 9448e827..f1508d66 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -7,24 +7,14 @@ your application. .. _Flask: http://flask.palletsprojects.com/ -Installation ------------- - -Install from PyPI using an installer such as pip: - -.. code-block:: text - - $ pip install Flask-Session - - Table of Contents ----------------- .. toctree:: :maxdepth: 2 + installation quickstart - interfaces config api license diff --git a/docs/installation.rst b/docs/installation.rst new file mode 100644 index 00000000..dadde718 --- /dev/null +++ b/docs/installation.rst @@ -0,0 +1,45 @@ + +Installation +============ + +Install from PyPI using an installer such as pip: + +.. code-block:: text + + $ pip install Flask-Session + +Unless you are using the FileSystemCache, you will also need to choose and a backend and install an appropriate client library. + +For example, if you want to use Redis as your backend, you will need to install the redis-py client library: + +.. code-block:: text + + $ pip install redis + + +Supported Backends and Client Libraries +--------------------------------------- + + +.. list-table:: + :header-rows: 1 + + * - Backend + - Client Library + * - Redis + - redis-py_ + * - Memcached + - pylibmc_, python-memcached_, pymemcache_ + * - MongoDB + - pymongo_ + * - SQL Alchemy + - flask-sqlalchemy_ + +Other clients may work if they use the same commands as the ones listed above. + +.. _redis-py: https://github.com/andymccurdy/redis-py +.. _pylibmc: http://sendapatch.se/projects/pylibmc/ +.. _python-memcached: https://github.com/linsomniac/python-memcached +.. _pymemcache: https://github.com/pinterest/pymemcache +.. _pymongo: http://api.mongodb.org/python/current/index.html +.. _Flask-SQLAlchemy: https://github.com/pallets-eco/flask-sqlalchemy \ No newline at end of file diff --git a/docs/interfaces.rst b/docs/interfaces.rst deleted file mode 100644 index ac4e3811..00000000 --- a/docs/interfaces.rst +++ /dev/null @@ -1,68 +0,0 @@ -Built-in Session Interfaces -=========================== - -.. currentmodule:: flask_session - - -:class:`NullSessionInterface` ------------------------------ - -If you do not configure a different ``SESSION_TYPE``, this will be used to -generate nicer error messages. Will allow read-only access to the empty -session but fail on setting. - - -:class:`RedisSessionInterface` ------------------------------- - -Uses the Redis key-value store as a session backend. (`redis-py`_ required) - -Relevant configuration values: - -- SESSION_REDIS - - -:class:`MemcachedSessionInterface` ----------------------------------- - -Uses the Memcached as a session backend. (`pylibmc`_ or `python-memcached`_ or `pymemcache` required) - -- SESSION_MEMCACHED - - -:class:`FileSystemSessionInterface` ------------------------------------ - -Uses the :class:`cachelib.file.FileSystemCache` as a session backend. - -- SESSION_FILE_DIR -- SESSION_FILE_THRESHOLD -- SESSION_FILE_MODE - - -:class:`MongoDBSessionInterface` --------------------------------- - -Uses the MongoDB as a session backend. (`pymongo`_ required) - -- SESSION_MONGODB -- SESSION_MONGODB_DB -- SESSION_MONGODB_COLLECT - -.. _redis-py: https://github.com/andymccurdy/redis-py -.. _pylibmc: http://sendapatch.se/projects/pylibmc/ -.. _memcache: https://github.com/linsomniac/python-memcached -.. _pymongo: http://api.mongodb.org/python/current/index.html - - -:class:`SqlAlchemySessionInterface` ------------------------------------ - -.. versionadded:: 0.2 - -Uses SQLAlchemy as a session backend. (`Flask-SQLAlchemy`_ required) - -- SESSION_SQLALCHEMY -- SESSION_SQLALCHEMY_TABLE - -.. _Flask-SQLAlchemy: https://pythonhosted.org/Flask-SQLAlchemy/ diff --git a/src/flask_session/__init__.py b/src/flask_session/__init__.py index 6bd9f407..60fb9fc7 100644 --- a/src/flask_session/__init__.py +++ b/src/flask_session/__init__.py @@ -9,7 +9,7 @@ SqlAlchemySessionInterface, ) -__version__ = "0.5.1" +__version__ = "0.6.0" class Session: diff --git a/src/flask_session/sessions.py b/src/flask_session/sessions.py index 3d5efe9e..19597491 100644 --- a/src/flask_session/sessions.py +++ b/src/flask_session/sessions.py @@ -8,6 +8,7 @@ import pickle from datetime import datetime, timezone + from flask.sessions import SessionInterface as FlaskSessionInterface from flask.sessions import SessionMixin from itsdangerous import BadSignature, Signer, want_bytes @@ -77,7 +78,12 @@ def _sign(self, app, sid): class NullSessionInterface(SessionInterface): - """Used to open a :class:`flask.sessions.NullSession` instance.""" + """Used to open a :class:`flask.sessions.NullSession` instance. + + If you do not configure a different ``SESSION_TYPE``, this will be used to + generate nicer error messages. Will allow read-only access to the empty + session but fail on setting. + """ def open_session(self, app, request): return None @@ -133,16 +139,19 @@ def fetch_session(self, sid): class RedisSessionInterface(ServerSideSessionInterface): - """Uses the Redis key-value store as a session backend. - - .. versionadded:: 0.2 - The `use_signer` parameter was added. + """Uses the Redis key-value store as a session backend. (`redis-py` required) :param redis: A ``redis.Redis`` instance. :param key_prefix: A prefix that is added to all Redis store keys. :param use_signer: Whether to sign the session id cookie or not. :param permanent: Whether to use permanent session or not. :param sid_length: The length of the generated session id in bytes. + + .. versionadded:: 0.6 + The `sid_length` parameter was added. + + .. versionadded:: 0.2 + The `use_signer` parameter was added. """ serializer = pickle @@ -208,16 +217,20 @@ def save_session(self, app, session, response): class MemcachedSessionInterface(ServerSideSessionInterface): - """A Session interface that uses memcached as backend. - - .. versionadded:: 0.2 - The `use_signer` parameter was added. + """A Session interface that uses memcached as backend. (`pylibmc` or `python-memcached` or `pymemcache` required) :param client: A ``memcache.Client`` instance. :param key_prefix: A prefix that is added to all Memcached store keys. :param use_signer: Whether to sign the session id cookie or not. :param permanent: Whether to use permanent session or not. :param sid_length: The length of the generated session id in bytes. + + .. versionadded:: 0.6 + The `sid_length` parameter was added. + + .. versionadded:: 0.2 + The `use_signer` parameter was added. + """ serializer = pickle @@ -239,7 +252,7 @@ def _get_preferred_memcache_client(self): for module_name, server in clients: try: module = __import__(module_name) - ClientClass = getattr(module, "Client") + ClientClass = module.Client return ClientClass(server) except ImportError: continue @@ -313,9 +326,6 @@ def save_session(self, app, session, response): class FileSystemSessionInterface(ServerSideSessionInterface): """Uses the :class:`cachelib.file.FileSystemCache` as a session backend. - .. versionadded:: 0.2 - The `use_signer` parameter was added. - :param cache_dir: the directory where session files are stored. :param threshold: the maximum number of items the session stores before it starts deleting some. @@ -324,6 +334,12 @@ class FileSystemSessionInterface(ServerSideSessionInterface): :param use_signer: Whether to sign the session id cookie or not. :param permanent: Whether to use permanent session or not. :param sid_length: The length of the generated session id in bytes. + + .. versionadded:: 0.6 + The `sid_length` parameter was added. + + .. versionadded:: 0.2 + The `use_signer` parameter was added. """ session_class = FileSystemSession @@ -394,10 +410,7 @@ def save_session(self, app, session, response): class MongoDBSessionInterface(ServerSideSessionInterface): - """A Session interface that uses mongodb as backend. - - .. versionadded:: 0.2 - The `use_signer` parameter was added. + """A Session interface that uses mongodb as backend. (`pymongo` required) :param client: A ``pymongo.MongoClient`` instance. :param db: The database you want to use. @@ -406,6 +419,12 @@ class MongoDBSessionInterface(ServerSideSessionInterface): :param use_signer: Whether to sign the session id cookie or not. :param permanent: Whether to use permanent session or not. :param sid_length: The length of the generated session id in bytes. + + .. versionadded:: 0.6 + The `sid_length` parameter was added. + + .. versionadded:: 0.2 + The `use_signer` parameter was added. """ serializer = pickle @@ -525,8 +544,6 @@ def save_session(self, app, session, response): class SqlAlchemySessionInterface(ServerSideSessionInterface): """Uses the Flask-SQLAlchemy from a flask app as a session backend. - .. versionadded:: 0.2 - :param app: A Flask app instance. :param db: A Flask-SQLAlchemy instance. :param table: The table name you want to use. @@ -537,6 +554,12 @@ class SqlAlchemySessionInterface(ServerSideSessionInterface): :param sequence: The sequence to use for the primary key if needed. :param schema: The db schema to use :param bind_key: The db bind key to use + + .. versionadded:: 0.6 + The `sid_length`, `sequence`, `schema` and `bind_key` parameters were added. + + .. versionadded:: 0.2 + The `use_signer` parameter was added. """ serializer = pickle