Login is impossible in pgAdmin 4 9.17 with Flask-Security-Too 5.8.2 because User.is_locked() has inverted semantics
Environment
- pgAdmin 4: 9.17
- Python: 3.14
- Installation: Python wheel / virtualenv
- Mode: Server
- Gunicorn: 26.0.0
- Flask-Security-Too: 5.8.2
- Flask-WTF: 1.3.0
- WTForms: 3.2.2
- Flask: 3.1.3
Description
An active internal Administrator user with a valid password cannot log in to pgAdmin.
Submitting correct credentials redirects the browser back to the login page:
POST /authenticate/login -> 302
Location: /login
Incorrect passwords are detected correctly.
However, when the correct password is entered, authentication still does not complete and the user is redirected back to /login.
User state
The affected user is valid and active:
{
"id": 2,
"username": "user@example.org",
"email": "user@example.org",
"active": true,
"role": "Administrator",
"auth_source": "internal",
"locked": false
}
The same behavior was reproduced with multiple internal users.
Password verification
The password was verified directly through Flask-Security using the same pgAdmin environment:
user_found: True
active: True
locked: False
login_attempts: 0
password_valid: True
Therefore the problem is not caused by an incorrect password, inactive account, or locked account.
Login form diagnostics
The installed flask_security.forms.LoginForm.validate() contains:
if self.user.is_locked(self.ifield.errors):
return False
This means the expected contract is:
is_locked() == True -> user is locked -> validation fails
is_locked() == False -> user is not locked -> validation continues
Direct diagnostics of the login form produced:
=== BASE VALIDATION ===
is_submitted: True
meta.csrf: False
base_valid: True
errors: {}
form_errors: []
=== FULL LOGIN VALIDATION ===
valid: False
errors: {}
form_errors: []
user: <User 2>
user_authenticated: True
requires_confirmation: False
The base WTForms validation succeeds and the password is successfully authenticated, but the complete login validation returns False.
Root cause
The pgAdmin User.is_locked() implementation appears to use the opposite return semantics from those expected by Flask-Security-Too 5.8.2.
The installed implementation behaved effectively like:
def is_locked(self, form_error=None):
if self.locked:
if form_error is not None:
form_error.append(
gettext(
'Your account is locked. Please contact the Administrator.'
)
)
return False
return True
For an unlocked user:
the method returns:
Flask-Security then executes:
if self.user.is_locked(self.ifield.errors):
return False
which causes every unlocked user to fail login validation.
Workaround
Changing the return values to the following immediately restores login functionality:
def is_locked(self, form_error=None):
if self.locked:
if form_error is not None:
form_error.append(
gettext(
'Your account is locked. Please contact the Administrator.'
)
)
return True
return False
After restarting pgAdmin, login works normally.
Expected behavior
An active, unlocked internal user with a valid password should authenticate successfully.
User.is_locked() should return:
True -> account is locked
False -> account is not locked
to match the contract expected by Flask-Security-Too's LoginForm.validate().
Actual behavior
For an unlocked user, User.is_locked() returns True.
As a result:
valid password
↓
user_authenticated = True
↓
user.is_locked(...) = True
↓
LoginForm.validate() = False
↓
302 Location: /login
Additional notes
This was reproduced with pgAdmin 4 9.17 installed from the Python package in a virtual environment and running in Server Mode through Gunicorn.
The workaround above is only a local vendor patch and will be overwritten when the pgAdmin package is upgraded or reinstalled.
Login is impossible in pgAdmin 4 9.17 with Flask-Security-Too 5.8.2 because
User.is_locked()has inverted semanticsEnvironment
Description
An active internal Administrator user with a valid password cannot log in to pgAdmin.
Submitting correct credentials redirects the browser back to the login page:
Incorrect passwords are detected correctly.
However, when the correct password is entered, authentication still does not complete and the user is redirected back to
/login.User state
The affected user is valid and active:
{ "id": 2, "username": "user@example.org", "email": "user@example.org", "active": true, "role": "Administrator", "auth_source": "internal", "locked": false }The same behavior was reproduced with multiple internal users.
Password verification
The password was verified directly through Flask-Security using the same pgAdmin environment:
Therefore the problem is not caused by an incorrect password, inactive account, or locked account.
Login form diagnostics
The installed
flask_security.forms.LoginForm.validate()contains:This means the expected contract is:
Direct diagnostics of the login form produced:
The base WTForms validation succeeds and the password is successfully authenticated, but the complete login validation returns
False.Root cause
The pgAdmin
User.is_locked()implementation appears to use the opposite return semantics from those expected by Flask-Security-Too 5.8.2.The installed implementation behaved effectively like:
For an unlocked user:
the method returns:
Flask-Security then executes:
which causes every unlocked user to fail login validation.
Workaround
Changing the return values to the following immediately restores login functionality:
After restarting pgAdmin, login works normally.
Expected behavior
An active, unlocked internal user with a valid password should authenticate successfully.
User.is_locked()should return:to match the contract expected by Flask-Security-Too's
LoginForm.validate().Actual behavior
For an unlocked user,
User.is_locked()returnsTrue.As a result:
Additional notes
This was reproduced with pgAdmin 4 9.17 installed from the Python package in a virtual environment and running in Server Mode through Gunicorn.
The workaround above is only a local vendor patch and will be overwritten when the pgAdmin package is upgraded or reinstalled.