From 14bc98a5b712f626903fbcebd595b296b5fe047a Mon Sep 17 00:00:00 2001 From: William Grant Date: Sat, 13 Jun 2026 07:53:45 +0000 Subject: [PATCH] fix: preserve URL password when stripping iam_auth in get_db MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### what `get_db` removes the `iam_auth` query param from the database URL by round-tripping it through `make_url(str(url).split("?")[0])`. SQLAlchemy's `str(url)` masks the password as `***`, so the rebuilt URL carries a masked password. Strip the flag with `url.difference_update_query(["iam_auth"])` instead, which drops it while preserving the password (and any other query params, e.g. `sslmode`). ### why With neither `iam_auth` nor an `ASSETDB_DBCRED_ARN`, `get_db` falls through to connecting with the URL as-is -- but that URL's password had been masked to `***`, so the connection failed authentication. A plain `user:password` `DATABASE_URL` never actually worked; prod dodged it because credentials always came from IAM or Secrets Manager, both of which re-set the password afterwards. This lets a plain URL connect directly -- e.g. the platform local-dev stack -- without seeding a Secrets Manager entry. ### testing Built the image and ran `get_env_db()` against a local Postgres with a plain `postgresql://user:password@host/db` URL (no `iam_auth`, no `ASSETDB_DBCRED_ARN`): `select 1` succeeds where it previously failed with "password authentication failed". The IAM and `dbcreds` paths are unchanged (they re-set credentials after this line). ### docs No docs needed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- redash/stacklet/auth.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/redash/stacklet/auth.py b/redash/stacklet/auth.py index 600093bcf1..f65e13425e 100644 --- a/redash/stacklet/auth.py +++ b/redash/stacklet/auth.py @@ -53,7 +53,11 @@ def get_db(dburi, dbcreds=None, disable_iam_auth=False, schema=None): return None url = sqlalchemy.engine.url.make_url(dburi) iam_auth = url.query.get("iam_auth") - url = sqlalchemy.engine.url.make_url(str(url).split("?")[0]) + # Drop the iam_auth flag (it isn't a real connect arg) while preserving the + # rest of the URL. Don't round-trip through str(url): that renders the + # password as "***", so a plain user:password URL -- no iam_auth, no + # dbcreds -- would otherwise connect with a masked password and fail auth. + url = url.difference_update_query(["iam_auth"]) params = {"json_serializer": json.dumps} # Add schema translation if schema is provided