fix: SharedServer feature parity columns and write guards#9835
fix: SharedServer feature parity columns and write guards#9835
Conversation
WalkthroughExtends the Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@web/migrations/versions/add_user_id_to_debugger_func_args_.py`:
- Around line 161-164: The migration defines the 'kerberos_conn' column with
server_default='false'; change this to use SQLAlchemy's portable boolean literal
by setting server_default=sa.false() on the sa.Column(...) for 'kerberos_conn'
(and ensure sa.false is available from the imported sa namespace if not
already). This ensures the default renders as a proper SQL boolean literal
across DB backends.
In `@web/pgadmin/browser/server_groups/servers/__init__.py`:
- Around line 1182-1184: The current truthiness check for passexec_expiration
collapses an explicit 0 to None; change the conditional to test explicitly for
None (e.g. use "if server.passexec_expiration is not None else None") so that a
value of 0 is preserved when building the properties dict for
passexec_expiration and will round-trip correctly through the properties API.
- Around line 230-231: The update path currently computes tag deltas using
server.tags (the owner's tags) which causes non-owner SharedServer.tags to be
rebuilt from the owner; change the merge base so that when handling a non-owner
update (where SharedServer exists and you assign server.kerberos_conn =
sharedserver.kerberos_conn and server.tags = sharedserver.tags) you compute tag
diffs against sharedserver.tags (i.e. use SharedServer.tags as the base) instead
of server.tags inside update(), and apply only the delta to the non-owner
SharedServer record to avoid overwriting its existing tags.
In `@web/pgadmin/model/__init__.py`:
- Around line 563-569: The SCHEMA_VERSION constant was not incremented after
adding new SharedServer columns (passexec_cmd, passexec_expiration,
kerberos_conn, tags, post_connection_sql), so update the module's SCHEMA_VERSION
(e.g., from 50 to 51) where it is defined so upgrades detect and run the
migration for the SharedServer changes; ensure the new version is used by
migration/upgrade logic and any tests referencing SCHEMA_VERSION are updated
accordingly.
🪄 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: 49a1e807-6d76-4299-b568-5be7ba3c9c6e
📒 Files selected for processing (5)
web/migrations/versions/add_user_id_to_debugger_func_args_.pyweb/pgadmin/browser/server_groups/servers/__init__.pyweb/pgadmin/browser/server_groups/servers/tests/test_shared_server_unit.pyweb/pgadmin/model/__init__.pyweb/pgadmin/utils/driver/psycopg3/__init__.py
Add passexec_cmd, passexec_expiration, kerberos_conn, tags, and post_connection_sql to SharedServer so non-owners get their own per-user values instead of inheriting the owner's. Drop the unused db_res column which was never overlaid or writable by non-owners. Key changes: - New Alembic migration (sharedserver_feature_parity) adds 5 columns, drops db_res, cleans up orphaned records. All operations idempotent. - Overlay copies new fields from SharedServer instead of suppressing - _owner_only_fields guard blocks non-owners from setting passexec_cmd, passexec_expiration, db_res, db_res_type via API - Non-owners can set post_connection_sql (runs under their own creds) - update_tags and flag_modified use sharedserver for non-owners - update() response returns sharedserver tags for non-owners - ServerManager passexec suppression with config.SERVER_MODE guard - UI: post_connection_sql editable for non-owners (readonly only when connected, not when shared) - SCHEMA_VERSION bumped to 51 - Comprehensive unit tests for overlay, write guards, and tag deltas
0fd0ac3 to
4c1705e
Compare
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
web/pgadmin/browser/server_groups/servers/__init__.py (1)
965-985:⚠️ Potential issue | 🟡 MinorReturn the overlaid record in the update response.
For non-owner edits, this block still serializes most node fields from the owner
serverrow. Onlytagswas switched tosharedserver or server, so updates to fields likeusername,role, orsave_passwordcome back stale until the next fetch.🔧 Suggested fix
+ node_server = server + if sharedserver is not None: + node_server = ServerModule.get_shared_server_properties( + server, sharedserver) + return jsonify( node=self.blueprint.generate_browser_node( - "%d" % (server.id), server.servergroup_id, - server.name, + "%d" % (node_server.id), node_server.servergroup_id, + node_server.name, server_icon_and_background( - connected, manager, sharedserver) - if _is_non_owner(server) - else server_icon_and_background( - connected, manager, server), + connected, manager, node_server), True, self.node_type, connected=connected, - shared=server.shared, - user_id=server.user_id, + shared=node_server.shared, + user_id=node_server.user_id, user=manager.user_info if connected else None, server_type='pg', # default server type - username=server.username, - role=server.role, - is_password_saved=bool(server.save_password), - description=server.comment, - tags=(sharedserver or server).tags + username=node_server.username, + role=node_server.role, + is_password_saved=bool(node_server.save_password), + description=node_server.comment, + tags=node_server.tags ) )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/pgadmin/browser/server_groups/servers/__init__.py` around lines 965 - 985, The response uses owner `server` fields even for non-owner edits so returned values (username, role, is_password_saved, description, tags, etc.) can be stale; when _is_non_owner(server) is true, pick the overlaid record (sharedserver or server) as the source and pass its attributes to self.blueprint.generate_browser_node and related calls (e.g., username, role, save_password -> is_password_saved, comment -> description, tags) and use that same source for server_icon_and_background instead of mixing owner and overlay; update the code around generate_browser_node to compute something like source = (sharedserver or server) when _is_non_owner(server) and read source.username, source.role, bool(source.save_password), source.comment, source.tags, and pass source into server_icon_and_background.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@web/pgadmin/browser/server_groups/servers/__init__.py`:
- Around line 965-985: The response uses owner `server` fields even for
non-owner edits so returned values (username, role, is_password_saved,
description, tags, etc.) can be stale; when _is_non_owner(server) is true, pick
the overlaid record (sharedserver or server) as the source and pass its
attributes to self.blueprint.generate_browser_node and related calls (e.g.,
username, role, save_password -> is_password_saved, comment -> description,
tags) and use that same source for server_icon_and_background instead of mixing
owner and overlay; update the code around generate_browser_node to compute
something like source = (sharedserver or server) when _is_non_owner(server) and
read source.username, source.role, bool(source.save_password), source.comment,
source.tags, and pass source into server_icon_and_background.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 0efc8367-6e92-495f-bcb0-eaee6d213a28
📒 Files selected for processing (6)
web/migrations/versions/sharedserver_feature_parity_.pyweb/pgadmin/browser/server_groups/servers/__init__.pyweb/pgadmin/browser/server_groups/servers/static/js/server.ui.jsweb/pgadmin/browser/server_groups/servers/tests/test_shared_server_unit.pyweb/pgadmin/model/__init__.pyweb/pgadmin/utils/driver/psycopg3/__init__.py
🚧 Files skipped from review as they are similar to previous changes (2)
- web/pgadmin/utils/driver/psycopg3/init.py
- web/pgadmin/model/init.py
Summary
SharedServermodel (passexec_cmd,passexec_expiration,kerberos_conn,tags,post_connection_sql) for feature parity withServer, so non-owners get per-user values via overlay instead of inheriting/suppressing owner fieldspassexec_cmd,passexec_expiration,post_connection_sql,db_res,db_res_type) via_owner_only_fieldswrite guard in_set_valid_attr_value()passexec/post_connection_sqlonServerManagerfor non-owners as defense-in-depth, including re-suppression aftermanager.update()in theconnect()endpointget_shared_server_properties()forNonesharedserverBuilds on top of #9830.
Test plan
cd web && python regression/runtests.py --pkg browser.server_groups.servers --modules test_shared_server_unit— all unit tests passpassexec_cmdorpost_connection_sqlvia PUT to/browser/server/obj/passexec_cmdkerberos_conn,tags,post_connection_sqlvalues🤖 Generated with Claude Code
Summary by CodeRabbit
Release Notes