From 0a8613818ac4e0a1f5db70f8d487ad692c7c924f Mon Sep 17 00:00:00 2001 From: amangalampalli-ks Date: Fri, 20 Mar 2026 17:04:33 +0530 Subject: [PATCH] Fix launch credential not updating on repeated edit of launch-user --- .../tunnel/port_forward/TunnelGraph.py | 33 +++++++++++++++++++ .../commands/tunnel_and_connections.py | 2 ++ 2 files changed, 35 insertions(+) diff --git a/keepercommander/commands/tunnel/port_forward/TunnelGraph.py b/keepercommander/commands/tunnel/port_forward/TunnelGraph.py index 8f86d83b4..2facf8705 100644 --- a/keepercommander/commands/tunnel/port_forward/TunnelGraph.py +++ b/keepercommander/commands/tunnel/port_forward/TunnelGraph.py @@ -458,6 +458,39 @@ def check_if_resource_has_launch_credential(self, resource_uid): return user_vertex.uid return False + def clear_launch_credential_for_resource(self, resource_uid, exclude_user_uid=None): + """Remove is_launch_credential from all users on a resource except exclude_user_uid.""" + resource_vertex = self.linking_dag.get_vertex(resource_uid) + if resource_vertex is None: + return + dirty = False + for user_vertex in resource_vertex.has_vertices(EdgeType.ACL): + if exclude_user_uid and user_vertex.uid == exclude_user_uid: + continue + acl_edge = user_vertex.get_edge(resource_vertex, EdgeType.ACL) + if not acl_edge: + continue + edge_content = acl_edge.content_as_dict + if edge_content and edge_content.get('is_launch_credential'): + edge_content = dict(edge_content) + edge_content.pop('is_launch_credential') + user_vertex.belongs_to(resource_vertex, EdgeType.ACL, content=edge_content) + dirty = True + if dirty: + self.linking_dag.save() + + def upgrade_resource_meta_to_v1(self, resource_uid): + """Ensure resource vertex meta has version >= 1 so vault reads ACL launch credentials.""" + resource_vertex = self.linking_dag.get_vertex(resource_uid) + if resource_vertex is None: + return + content = get_vertex_content(resource_vertex) + if content and content.get('version', 0) >= RESOURCE_META_VERSION_V1: + return + upgraded = ensure_resource_meta_v1(content) + resource_vertex.add_data(content=upgraded, path='meta', needs_encryption=False) + self.linking_dag.save() + def check_if_resource_allowed(self, resource_uid, setting): resource_vertex = self.linking_dag.get_vertex(resource_uid) content = get_vertex_content(resource_vertex) diff --git a/keepercommander/commands/tunnel_and_connections.py b/keepercommander/commands/tunnel_and_connections.py index 48cd8b2dd..413a709b0 100644 --- a/keepercommander/commands/tunnel_and_connections.py +++ b/keepercommander/commands/tunnel_and_connections.py @@ -1046,7 +1046,9 @@ def execute(self, params, **kwargs): f'{bcolors.FAIL}Launch user record must be a pamUser record type.{bcolors.ENDC}') launch_uid = launch_rec.record_uid if record_type in ("pamDatabase", "pamDirectory", "pamMachine"): + tdag.clear_launch_credential_for_resource(record_uid, exclude_user_uid=launch_uid) tdag.link_user_to_resource(launch_uid, record_uid, is_launch_credential=True, belongs_to=True) + tdag.upgrade_resource_meta_to_v1(record_uid) # Print out PAM Settings if not kwargs.get("silent", False): tdag.print_tunneling_config(record_uid, record.get_typed_field('pamSettings'), config_uid)