Why does Flask-Security invoke a new KVSession entry for each request?

I am trying to use Flask-KVSession as an alternative session implementation for the Flask website. I created a test site (see Code 1 ). When I launched this, I can use a browser to store values ​​in a session, navigating between various resources in my web browser. This is working correctly. Also, when I look at the sessions table in the resulting SQLite database, I see the only record that was used to store this session all the time.

Then I try to add Flask-Security to this (see Code 2 below). After starting this site (be sure to delete the existing sqlite test.db file first), I am presented with a login prompt and I am logged in. Then I go to the same thing as switching between resources. I get the same results.

The problem is that when I look in the sqlitebrowser sessions table, there are 8 entries. It turns out that a new session record was created for every request that was requested.

Why is a new session entry created for each request when using Flask-Security? Why is the updated session not updated as before?

Code 1 (KVSession without flag protection)

 import os from flask import Flask, session app = Flask(__name__) app.secret_key = os.urandom(64) ############# # SQLAlchemy ############# from flask.ext.sqlalchemy import SQLAlchemy db = SQLAlchemy(app) DB_DIR = os.path.dirname(os.path.abspath(__file__)) DB_URI = 'sqlite:////{0}/test.db'.format(DB_DIR) app.config['SQLALCHEMY_DATABASE_URI'] = DB_URI @app.before_first_request def create_user(): db.create_all() ############ # KVSession ############ from simplekv.db.sql import SQLAlchemyStore from flask.ext.kvsession import KVSessionExtension store = SQLAlchemyStore(db.engine, db.metadata, 'sessions') kvsession = KVSessionExtension(store, app) @app.route('/a') def a(): session['last'] = 'b' return 'Thank you for visiting A!' @app.route('/b') def b(): session['last'] = 'b' return 'Thank you for visiting B!' @app.route('/c') def c(): return 'You last visited "{0}"'.format(session['last']) app.run(debug=True) 

Code 2 (KVSession WITH Flask-Security)

 import os from flask import Flask, session app = Flask(__name__) app.secret_key = os.urandom(64) ############# # SQLAlchemy ############# from flask.ext.sqlalchemy import SQLAlchemy db = SQLAlchemy(app) DB_DIR = os.path.dirname(os.path.abspath(__file__)) DB_URI = 'sqlite:////{0}/test.db'.format(DB_DIR) app.config['SQLALCHEMY_DATABASE_URI'] = DB_URI ########### # Security ########### # This import needs to happen after SQLAlchemy db is created above from flask.ext.security import ( Security, SQLAlchemyUserDatastore, current_user, UserMixin, RoleMixin, login_required ) # Define models roles_users = db.Table('roles_users', db.Column('user_id', db.Integer(), db.ForeignKey('user.id')), db.Column('role_id', db.Integer(), db.ForeignKey('role.id'))) class Role(db.Model, RoleMixin): id = db.Column(db.Integer(), primary_key=True) name = db.Column(db.String(80), unique=True) description = db.Column(db.String(255)) class User(db.Model, UserMixin): id = db.Column(db.Integer, primary_key=True) email = db.Column(db.String(255), unique=True) password = db.Column(db.String(255)) active = db.Column(db.Boolean()) confirmed_at = db.Column(db.DateTime()) roles = db.relationship('Role', secondary=roles_users, backref=db.backref('users', lazy='dynamic')) user_datastore = SQLAlchemyUserDatastore(db, User, Role) security = Security(app, user_datastore) @app.before_first_request def create_user(): db.create_all() user_datastore.create_user(email=' test@example.com ', password='password') db.session.commit() ############ # KVSession ############ from simplekv.db.sql import SQLAlchemyStore from flask.ext.kvsession import KVSessionExtension store = SQLAlchemyStore(db.engine, db.metadata, 'sessions') kvsession = KVSessionExtension(store, app) @app.route('/a') @login_required def a(): session['last'] = 'b' return 'Thank you for visiting A!' @app.route('/b') @login_required def b(): session['last'] = 'b' return 'Thank you for visiting B!' @app.route('/c') @login_required def c(): return 'You last visited "{0}"'.format(session['last']) app.run(debug=True) 

Version Information

 Python 2.7.3 Flask==0.9 Flask==0.9 Flask-KVSession==0.3.2 Flask-Login==0.1.3 Flask-Mail==0.8.2 Flask-Principal==0.3.5 Flask-SQLAlchemy==0.16 Flask-Security==1.6.3 SQLAlchemy==0.8.1 
+4
source share
1 answer

It turns out this is due to a known issue with flask-login (using flags) when the login flag is used with a session storage library such as KVSession.

Basically, KVSession needs to update the database with new session information when the data in the session is created or modified. And in the above example, this happens correctly: the first time I click on the page, the session is created. After that, the existing session is updated.

However, in the background, the browser sends a cookie-less request to my web server that requires my icon. Therefore, the bulb processes the request /favicon.ico . This request (or any other request that will be 404) is still being processed by the bulb. This means that the login checkbox will look at the request and try to do its magic.

It so happens that the login checkbox does not try to insert anything into the session, but it still WATCHES how the session was changed in relation to KVSession. Since it SEEES as a session change, KVSession updates the database. Below is the code from the login checkbox:

 def _update_remember_cookie(self, response): operation = session.pop("remember", None) ... 

The _update_remember_cookie method _update_remember_cookie called during the request life cycle. Although session.pop will not change the session if there is no β€œremember” key in the session (which it does not do in this case), KVSession still sees the pop and assumes that the session is changing.

The problem for flask-login provides a simple bug fix, but it was not entered into the flash drive login. It seems that the attendant is looking for a complete correspondence and implements it there.

+6
source

Source: https://habr.com/ru/post/1482178/


All Articles