From f7a95a7b618d363f0b0383759612f606c0efbace Mon Sep 17 00:00:00 2001 From: Thomas Han Date: Sat, 23 May 2026 09:43:37 +1200 Subject: [PATCH 1/2] fix(import-export): accept Username or SharedUsername for shared servers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit validate_json_data required Username for all non-Service connections, rejecting legitimate JSON that only carried SharedUsername. Accept either attribute when Shared is true; keep Username required for non-shared servers. Extract is_shared as a local while we're here — it's now referenced twice in the loop body. Co-Authored-By: Claude Opus 4.7 --- web/pgadmin/utils/__init__.py | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/web/pgadmin/utils/__init__.py b/web/pgadmin/utils/__init__.py index 7f2136d99f5..edeee27f139 100644 --- a/web/pgadmin/utils/__init__.py +++ b/web/pgadmin/utils/__init__.py @@ -601,8 +601,10 @@ def validate_json_data(data, is_admin): for server in data["Servers"]: obj = data["Servers"][server] + is_shared = obj.get("Shared", None) + # Check if server is shared.Won't import if user is non-admin - if obj.get('Shared', None) and not is_admin: + if is_shared and not is_admin: print("Won't import the server '%s' as it is shared " % obj["Name"]) skip_servers.append(server) @@ -627,14 +629,25 @@ def check_is_integer(value): is_service_attrib_available = obj.get("Service", None) is not None if not is_service_attrib_available: - for attrib in ("Port", "Username"): - errmsg = check_attrib(attrib) + errmsg = check_attrib("Port") + if errmsg: + return errmsg + errmsg = check_is_integer(obj["Port"]) + if errmsg: + return errmsg + + if is_shared: + # Shared servers may carry either the owner's username + # or a per-user override, so accept either attribute. + if "Username" not in obj and "SharedUsername" not in obj: + return gettext( + "'Username' or 'SharedUsername' attribute not " + "found for server '%s'" % server + ) + else: + errmsg = check_attrib("Username") if errmsg: return errmsg - if attrib == 'Port': - errmsg = check_is_integer(obj[attrib]) - if errmsg: - return errmsg errmsg = check_attrib("MaintenanceDB") if errmsg: From 177b57dfc5388c2bc2a7f68e2623de45c3d242a3 Mon Sep 17 00:00:00 2001 From: Thomas Han Date: Sat, 23 May 2026 10:23:48 +1200 Subject: [PATCH 2/2] fix(import-export): set username fallback to SharedUsername for shared servers When loading database servers from an import file, fall back to SharedUsername for the underlying server.username when the server is shared and no explicit Username is provided. Co-Authored-By: Claude Opus 4.7 --- web/pgadmin/utils/__init__.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/web/pgadmin/utils/__init__.py b/web/pgadmin/utils/__init__.py index edeee27f139..7ee4beb7afd 100644 --- a/web/pgadmin/utils/__init__.py +++ b/web/pgadmin/utils/__init__.py @@ -733,6 +733,11 @@ def load_database_servers(input_file, selected_servers, groups_added = groups_added + 1 groups = ServerGroup.query.filter_by(user_id=user_id) + is_shared = obj.get("Shared", None) + username = obj.get("Username", None) + if is_shared and not username: + username = obj.get("SharedUsername", None) + # Create the server new_server = Server() new_server.name = obj["Name"] @@ -744,7 +749,7 @@ def load_database_servers(input_file, selected_servers, new_server.port = obj.get("Port", None) - new_server.username = obj.get("Username", None) + new_server.username = username new_server.role = obj.get("Role", None) @@ -798,7 +803,7 @@ def load_database_servers(input_file, selected_servers, new_server.tunnel_keep_alive = \ obj.get("TunnelKeepAlive", None) - new_server.shared = obj.get("Shared", None) + new_server.shared = is_shared new_server.shared_username = obj.get("SharedUsername", None)