Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions web/pgadmin/browser/server_groups/servers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1685,8 +1685,16 @@ def connect(self, gid, sid, is_qt=False, server=None):
password = conn_passwd or server.password
else:
password = data['password'] if 'password' in data else None
save_password = data['save_password']\
if 'save_password' in data else False
# The password-prompt dialog doesn't resend the server's
# existing save_password setting, only its own checkbox
# state. If the server is already configured to save its
# password, a freshly entered replacement should still be
# persisted, or the stale saved password is never
# replaced. See issue #10128.
save_password = (
data['save_password'] if 'save_password' in data
else False
) or bool(server.save_password)

try:
# Encrypt the password before saving with user's login
Expand Down Expand Up @@ -1756,6 +1764,16 @@ def connect(self, gid, sid, is_qt=False, server=None):

return internal_server_error(errormsg=str(e))

# Persist the manager's corrected in-memory password (set by
# the driver on a successful connect) into the Flask session.
# Without this, a fresh worker process handling the next
# request (e.g. opening the Query Tool, in a multi-worker
# deployment) restores the stale pre-fix manager state from
# the session and the new password is lost, re-triggering the
# password prompt indefinitely. See issue #10128.
if password:
manager.update_session()

if save_tunnel_password and config.ALLOW_SAVE_TUNNEL_PASSWORD:
try:
# Save the encrypted tunnel password.
Expand Down
41 changes: 38 additions & 3 deletions web/pgadmin/tools/sqleditor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2712,7 +2712,7 @@ def connect_server(sid):
# password the user just entered at that prompt is cached here so the
# tool's connection can use it, instead of being discarded and
# re-prompted in a loop.
_cache_manager_password_from_request(manager)
_cache_manager_password_from_request(manager, server)
return make_json_response(
success=1,
info=gettext("Server connected."),
Expand All @@ -2725,7 +2725,7 @@ def connect_server(sid):
)


def _cache_manager_password_from_request(manager):
def _cache_manager_password_from_request(manager, server=None):
"""
Cache the password supplied with the current request (from a tool's
password prompt) onto the server manager, so that connections opened by
Expand All @@ -2736,6 +2736,13 @@ def _cache_manager_password_from_request(manager):
password, so a freshly entered credential (e.g. a regenerated, short-lived
cloud auth token) takes effect immediately.

When "Save Password" is requested and allowed, the freshly entered
password is also persisted to the server record (overwriting any stale
stored ciphertext). Without this, a rotated/regenerated password entered
at the prompt would work for the current session only and the tool would
keep re-using the stale saved password and re-prompt on the next
connection.

This is best-effort: any failure (including malformed request data) is
logged and swallowed so it never turns the caller's "Server connected"
response into a 500 error.
Expand All @@ -2756,12 +2763,40 @@ def _cache_manager_password_from_request(manager):
if not crypt_key_present:
return

manager._update_password(encrypt(password, crypt_key))
enc_password = encrypt(password, crypt_key)
manager._update_password(enc_password)
manager.update_session()

# Persist the freshly entered password if the user asked to save it,
# so the stale stored ciphertext is replaced.
save_password = data.get('save_password', False)
if save_password in ('true', 'True', '1', 1, True) and \
ALLOW_SAVE_PASSWORD and server is not None:
_persist_saved_password(server, enc_password)
except Exception as e:
current_app.logger.exception(e)


def _persist_saved_password(server, enc_password):
"""
Persist the encrypted password to the server record (owned or shared),
replacing any stale stored ciphertext.
"""
from pgadmin.model import db
from pgadmin.browser.server_groups.servers import ServerModule

target = server
if server.shared and server.user_id != current_user.id:
shared_server = ServerModule.get_shared_server(
server, server.servergroup_id)
if shared_server is not None:
target = shared_server

setattr(target, 'save_password', 1)
setattr(target, 'password', enc_password)
db.session.commit()


@blueprint.route(
'/filter_dialog/<int:trans_id>',
methods=["PUT"], endpoint='set_filter_data'
Expand Down
Loading