Skip to content

Fix Query Tool password re-prompt loop for unsaved passwords (#9091)#10073

Open
dpage wants to merge 2 commits into
pgadmin-org:masterfrom
dpage:fix-9091-qt-password-prompt-loop
Open

Fix Query Tool password re-prompt loop for unsaved passwords (#9091)#10073
dpage wants to merge 2 commits into
pgadmin-org:masterfrom
dpage:fix-9091-qt-password-prompt-loop

Conversation

@dpage

@dpage dpage commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes an infinite password-prompt loop in the Query Tool (and other tools that open their own connections) for servers whose password was not saved — most visibly reported with long, short-lived cloud auth tokens used as the password (AWS RDS IAM, Azure Entra).

Root cause

When a tool such as the Query Tool opens a connection for a server whose password is not saved, it relies on the password cached on the ServerManager. If that cached password is unavailable, the tool returns 428 and the UI prompts for the password. The entered password is POSTed to the sqleditor.connect_server endpoint.

connect_server short-circuits with "Server connected" whenever the server's primary connection is already established — and in doing so silently discards the password the user just entered. The tool's own connection therefore still has no password and re-prompts immediately, producing an infinite loop in which the re-entered password appears to be rejected.

Fix

In that short-circuit path, cache the entered password on the ServerManager (encrypted) so the tool's connection can reuse it. The password overwrites any previously cached value, so a regenerated short-lived token takes effect immediately rather than the loop retrying a stale/expired one.

The change is confined to the connect_server endpoint via a small helper; it is a no-op when no password is supplied or the encryption key is unavailable.

Test plan

  • New unit test test_cache_manager_password.py (5 scenarios): password supplied is encrypted and cached; an existing cached password is overwritten; no-ops when the password is absent, empty, or the crypt key is missing. The cached value is verified to decrypt back to the original token intact.
  • Existing test_new_connection_dialog still passes.
  • pycodestyle clean.

Note for reviewers

Several reporters (and maintainers) could not reproduce the original symptom in every environment, and a length/content correlation in the thread remains unexplained. This change definitively breaks the prompt loop and makes a correctly re-entered password take effect, matching the primary reported symptom. I've asked the reporters on the issue for their server mode / worker count / workspace-restore details in case a complementary fix (more reliable manager.password persistence) is also warranted.

Closes #9091

Summary by CodeRabbit

Release Notes

  • Bug Fixes
    • Fixed Query Tool password prompt loop: users are no longer repeatedly prompted for a password when the primary connection is already established. The entered password is now cached to prevent unnecessary re-prompts.

…n-org#9091)

When a tool (Query Tool, View/Edit Data, etc.) opens a connection for a
server whose password was not saved, it relies on the password cached on
the server manager. If that cached password is unavailable, the tool
prompts for it. The entered password was POSTed to the connect_server
endpoint, which short-circuited with "Server connected" whenever the
server's primary connection was already established -- silently discarding
the entered password. The tool's own connection therefore still had no
password and re-prompted immediately, producing an infinite prompt loop
in which the re-entered password appeared to be rejected.

Cache the entered password on the server manager (encrypted) in that
short-circuit path so the tool's connection can reuse it. The password
overwrites any cached value, so a regenerated short-lived cloud auth
token (AWS RDS IAM / Azure Entra) takes effect immediately.
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f5a431ee-a737-4120-8e87-b325d7a712ae

📥 Commits

Reviewing files that changed from the base of the PR and between 66223ca and 0288142.

📒 Files selected for processing (2)
  • web/pgadmin/tools/sqleditor/__init__.py
  • web/pgadmin/tools/sqleditor/utils/tests/test_cache_manager_password.py
 ____________________________________________________________________________________________________________________________________________________________________________________________
< The object-oriented model makes it easy to build up programs by accretion. What this often means, in practice, is that it provides a structured way to write spaghetti code. - Paul Graham >
 --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  \
   \   \
        \ /\
        ( )
      .( o ).

Walkthrough

This pull request fixes a regression where long passwords (such as AWS RDS authentication tokens) were not cached between initial server connection and subsequent SQL Editor queries, causing re-prompts and authentication failures. The fix extracts the password from client requests and stores it on the server manager when the primary connection is already established.

Changes

Password Caching for Query Tool

Layer / File(s) Summary
Password caching helper function and imports
web/pgadmin/tools/sqleditor/__init__.py
Added imports for encrypt and get_crypt_key utilities; implemented _cache_manager_password_from_request(manager) to extract password from request form or JSON, handle missing passwords or encryption keys gracefully, encrypt and store the password on the manager, and update the manager session.
Integration into connect_server
web/pgadmin/tools/sqleditor/__init__.py
When the server connection is already established in connect_server, invoke the password caching helper to persist the entered password for reuse by tool connections.
Regression test for password caching
web/pgadmin/tools/sqleditor/utils/tests/test_cache_manager_password.py
CacheManagerPasswordTest validates that submitted passwords are encrypted and cached when the crypto key is available, verifies decryption matches the original submitted token, and confirms no-op behavior when passwords are missing or the encryption key is unavailable.
Release notes documentation
docs/en_US/release_notes_9_16.rst
Document the fix for Issue #9091 under v9.16 bug fixes.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: fixing the password re-prompt loop in Query Tool with a specific reference to the issue.
Linked Issues check ✅ Passed The code changes directly address the core requirement from issue #9091: caching entered passwords on the server manager to prevent re-prompts when tools open connections to already-connected servers.
Out of Scope Changes check ✅ Passed All changes are focused and in-scope: password caching helper, module imports, release notes entry, and comprehensive unit tests all directly support the password prompt loop fix.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
web/pgadmin/tools/sqleditor/utils/tests/test_cache_manager_password.py (1)

37-64: ⚡ Quick win

Consider adding a JSON data scenario for complete coverage.

The stack context indicates that _cache_manager_password_from_request should extract passwords from "request form or JSON," but all current scenarios only test the form data path. Consider adding a scenario that supplies the password via request.data (JSON) instead of request.form to ensure both extraction paths are covered.

📝 Suggested JSON scenario
+        ('When a password is supplied via JSON it is encrypted and cached', dict(
+            form_data={},
+            json_data={'password': LONG_TOKEN},
+            crypt_key_present=True,
+            expect_cached=True,
+        )),

Then update line 77 to:

-        mock_request.data = None
+        mock_request.data = getattr(self, 'json_data', {})

Note: The RUF012 warning about mutable class attributes is a false positive. The scenarios pattern is an established convention in pgAdmin's BaseTestGenerator-based tests, where each test run receives independent scenario instances.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/pgadmin/tools/sqleditor/utils/tests/test_cache_manager_password.py`
around lines 37 - 64, Add a test scenario that covers JSON payload extraction by
supplying the password in request.data instead of request.form: update the
scenarios list (alongside the existing form-based cases) to include a case where
form_data={} (or omitted) and json_data={'password': LONG_TOKEN}, with
crypt_key_present=True and expect_cached=True so
_cache_manager_password_from_request is exercised for the JSON path; ensure any
existing_password/expect_cached variants you need are mirrored if you want
overwrite behavior tested.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@web/pgadmin/tools/sqleditor/__init__.py`:
- Around line 2708-2739: The function _cache_manager_password_from_request
currently calls json.loads(request.data) outside the try/except, which can raise
JSONDecodeError and crash the endpoint; move the JSON parsing into the existing
try/except (or add a small try/except around json.loads) so that invalid JSON is
treated as a no-op and the function returns silently; ensure you catch
json.JSONDecodeError (or Exception) and return early, keeping
manager._update_password(encrypt(...)) and manager.update_session inside the try
block so they only run with valid parsed data.

---

Nitpick comments:
In `@web/pgadmin/tools/sqleditor/utils/tests/test_cache_manager_password.py`:
- Around line 37-64: Add a test scenario that covers JSON payload extraction by
supplying the password in request.data instead of request.form: update the
scenarios list (alongside the existing form-based cases) to include a case where
form_data={} (or omitted) and json_data={'password': LONG_TOKEN}, with
crypt_key_present=True and expect_cached=True so
_cache_manager_password_from_request is exercised for the JSON path; ensure any
existing_password/expect_cached variants you need are mirrored if you want
overwrite behavior tested.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 18d32ae9-cbc2-4d7f-bc6e-5304c382f603

📥 Commits

Reviewing files that changed from the base of the PR and between 04fa05c and 66223ca.

📒 Files selected for processing (3)
  • docs/en_US/release_notes_9_16.rst
  • web/pgadmin/tools/sqleditor/__init__.py
  • web/pgadmin/tools/sqleditor/utils/tests/test_cache_manager_password.py

Comment thread web/pgadmin/tools/sqleditor/__init__.py
Move request-body JSON parsing inside the try/except so malformed request
data is treated as a silent no-op (logged and swallowed) rather than
propagating a JSONDecodeError and turning the connect_server endpoint's
'Server connected' response into a 500. Add a regression test scenario for
the malformed-JSON case.

Addresses CodeRabbit review feedback on pgadmin-org#10073.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes an infinite password re-prompt loop affecting Query Tool (and other tool-specific connections) when a server password is not saved, by caching the user-entered password on the ServerManager even when the primary connection is already established.

Changes:

  • Cache the password from the connect_server request on the ServerManager (encrypted) in the “already connected” short-circuit path.
  • Add unit tests covering caching, overwriting, and no-op scenarios (including malformed JSON handling).
  • Document the fix in the v9.16 release notes.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
web/pgadmin/tools/sqleditor/init.py Add helper to cache request password (encrypted) when connect_server short-circuits as already connected.
web/pgadmin/tools/sqleditor/utils/tests/test_cache_manager_password.py Add regression tests for password caching behavior across multiple scenarios.
docs/en_US/release_notes_9_16.rst Add release note entry for Issue #9091.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +2741 to +2742
except Exception as e:
current_app.logger.exception(e)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Very long password not working in Query Tool in v9.6 and newer

2 participants