Since updating my Flask app's dependencies, my Sentry is logging errors I don't understand. Changes to Flask-Login or Flask-Security-Too have thrown me off.
My user model has always used an Integer as its primary_key
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
I'm getting the following error:
(psycopg2.errors.InvalidTextRepresentation) invalid input syntax for integer: "5818f9aad20447b1b6a37c51f2c2xxxx"
LINE 3: WHERE "user".id = '5818f9aad20447b1b6a37c51f2c2xxxx'
Which I assume stems from Flask-Security-Too's datastore user lookup:
def find_user(self, **kwargs):
return self.user_model.query.filter_by(**kwargs).first()
But maybe the problem goes bath further to Flask-Login as it's getting this random ID from the session:
# Load user from Flask Session
user_id = session.get("_user_id")
if user_id is not None and self._user_callback is not None:
user = self._user_callback(user_id)
Is Flask-Security-Too's fs_uniquifier
in my session? Is that what's stepping into the place of my integer ID? I'm hoping I can avoid refactoring my entire code and database and retain my user.id
as an int. Any thoughts are appreciated.
Here is the full trace that has occurred 15 times since upgrading, all without halting users from accessing or submitting data:
InvalidTextRepresentation: invalid input syntax for integer: "63b93cd01b8f4e65a7daa83becc3xxxx"
LINE 3: WHERE "user".id = '63b93cd01b8f4e65a7daa83becc3xxxx'
^
File "sqlalchemy/engine/base.py", line 1819, in _execute_context
self.dialect.do_execute(
File "sqlalchemy/engine/default.py", line 732, in do_execute
cursor.execute(statement, parameters)
DataError: (psycopg2.errors.InvalidTextRepresentation) invalid input syntax for integer: "63b93cd01b8f4e65a7daa83becc3xxxx"
LINE 3: WHERE "user".id = '63b93cd01b8f4e65a7daa83becc3xxxx'
^
[SQL: SELECT "user".id AS user_id, "user".school_id AS user_school_id, "user".location_id AS user_location_id, "user".first_name AS user_first_name, "user".last_name AS user_last_name, "user".display_name AS user_display_name, "user".image AS user_image, "user".email AS user_email, "user".password AS use...
File "flask/app.py", line 2463, in wsgi_app
response = self.full_dispatch_request()
File "flask/app.py", line 1760, in full_dispatch_request
rv = self.handle_user_exception(e)
File "__init__.py", line 271, in error_router
return original_handler(e)
File "flask/app.py", line 1756, in full_dispatch_request
rv = self.preprocess_request()
File "flask/app.py", line 2247, in preprocess_request
rv = self.ensure_sync(before_func)()
File "flask_principal.py", line 477, in _on_before_request
identity = loader()
File "flask_security/core.py", line 245, in _identity_loader
if not isinstance(current_user._get_current_object(), AnonymousUserMixin):
File "werkzeug/local.py", line 516, in _get_current_object
return get_name(local()) # type: ignore
File "flask_login/utils.py", line 25, in <lambda>
current_user = LocalProxy(lambda: _get_user())
File "flask_login/utils.py", line 372, in _get_user
current_app.login_manager._load_user()
File "flask_login/login_manager.py", line 364, in _load_user
user = self._user_callback(user_id)
File "flask_security/core.py", line 221, in _user_loader
return _security.datastore.find_user(id=user_id)
File "flask_security/datastore.py", line 254, in find_user
return self.user_model.query.filter_by(**kwargs).first()
File "sqlalchemy/orm/query.py", line 2819, in first
return self.limit(1)._iter().first()
File "sqlalchemy/orm/query.py", line 2903, in _iter
result = self.session.execute(
File "sqlalchemy/orm/session.py", line 1712, in execute
result = conn._execute_20(statement, params or {}, execution_options)
File "sqlalchemy/engine/base.py", line 1631, in _execute_20
return meth(self, args_10style, kwargs_10style, execution_options)
File "sqlalchemy/sql/elements.py", line 332, in _execute_on_connection
return connection._execute_clauseelement(
File "sqlalchemy/engine/base.py", line 1498, in _execute_clauseelement
ret = self._execute_context(
File "sqlalchemy/engine/base.py", line 1862, in _execute_context
self._handle_dbapi_exception(
File "sqlalchemy/engine/base.py", line 2043, in _handle_dbapi_exception
util.raise_(
File "sqlalchemy/util/compat.py", line 208, in raise_
raise exception
File "sqlalchemy/engine/base.py", line 1819, in _execute_context
self.dialect.do_execute(
File "sqlalchemy/engine/default.py", line 732, in do_execute
cursor.execute(statement, parameters)
InFailedSqlTransaction: current transaction is aborted, commands ignored until end of transaction block
File "sqlalchemy/engine/base.py", line 1819, in _execute_context
self.dialect.do_execute(
File "sqlalchemy/engine/default.py", line 732, in do_execute
cursor.execute(statement, parameters)
InternalError: (psycopg2.errors.InFailedSqlTransaction) current transaction is aborted, commands ignored until end of transaction block
[SQL: SELECT "user".id AS user_id, "user".school_id AS user_school_id, "user".location_id AS user_location_id, "user".first_name AS user_first_name, "user".last_name AS user_last_name, "user".display_name AS user_display_name, "user".image AS user_image, "user".email AS user_email, "user".password AS user_password, "user".fs_uniquifier AS user_fs_uniquifier, "user".phone AS user_phone...
File "flask/app.py", line 2486, in __call__
return self.wsgi_app(environ, start_response)
File "flask/app.py", line 2466, in wsgi_app
response = self.handle_exception(e)
File "__init__.py", line 271, in error_router
return original_handler(e)
File "flask/app.py", line 1662, in handle_exception
server_error = self.ensure_sync(handler)(server_error)
File "__init__.py", line 21, in crash_page
return render_template('error_pages/500.html'), 500
File "flask/templating.py", line 147, in render_template
return _render(app, template, context)
File "flask/templating.py", line 128, in _render
app.update_template_context(context)
File "flask/app.py", line 932, in update_template_context
context.update(func())
File "flask_login/utils.py", line 407, in _user_context_processor
return dict(current_user=_get_user())
File "flask_login/utils.py", line 372, in _get_user
current_app.login_manager._load_user()
File "flask_login/login_manager.py", line 364, in _load_user
user = self._user_callback(user_id)
File "flask_security/core.py", line 221, in _user_loader
return _security.datastore.find_user(id=user_id)
File "flask_security/datastore.py", line 254, in find_user
return self.user_model.query.filter_by(**kwargs).first()
File "sqlalchemy/orm/query.py", line 2819, in first
return self.limit(1)._iter().first()
File "sqlalchemy/orm/query.py", line 2903, in _iter
result = self.session.execute(
File "sqlalchemy/orm/session.py", line 1712, in execute
result = conn._execute_20(statement, params or {}, execution_options)
File "sqlalchemy/engine/base.py", line 1631, in _execute_20
return meth(self, args_10style, kwargs_10style, execution_options)
File "sqlalchemy/sql/elements.py", line 332, in _execute_on_connection
return connection._execute_clauseelement(
File "sqlalchemy/engine/base.py", line 1498, in _execute_clauseelement
ret = self._execute_context(
File "sqlalchemy/engine/base.py", line 1862, in _execute_context
self._handle_dbapi_exception(
File "sqlalchemy/engine/base.py", line 2043, in _handle_dbapi_exception
util.raise_(
File "sqlalchemy/util/compat.py", line 208, in raise_
raise exception
File "sqlalchemy/engine/base.py", line 1819, in _execute_context
self.dialect.do_execute(
File "sqlalchemy/engine/default.py", line 732, in do_execute
cursor.execute(statement, parameters)
Some additional info - as of FS2 3.4.0 it no longer references user.id at all - so that can be whatever you want. FS2 overrides flask_login's UserMixin::get_id - and returns the value of fs_uniquifier. And yes - that is what is stored in the session cookie - and no - nothing in the session cookie is encrypted (it is signed). So: 1) are you overriding get_id()? 2) any chance that since it seems to be 'working' that you have some post-processing - maybe in a signal or something and it is calling the find_user or some other DB query?