Bug fix for Revoke OTP resolved, no longer crashes when select all is chosen when there are no active sessions, Swapped Revoke OTP and Quiet Hosts locations on menus

This commit is contained in:
2025-12-05 15:30:24 -05:00
parent ab5f00d8e7
commit 154a7efcc8
3 changed files with 35 additions and 36 deletions
+30 -31
View File
@@ -58,9 +58,14 @@ class OTPRevokeWidget(Static):
} }
#button_container { #button_container {
height: auto; height: auto;
width: 100%;
padding: 1; padding: 1;
align: center middle; align: center middle;
} }
#button_container Button {
min-width: 16;
margin: 0 1;
}
#result_container { #result_container {
height: auto; height: auto;
max-height: 10; max-height: 10;
@@ -87,29 +92,10 @@ class OTPRevokeWidget(Static):
# Action buttons # Action buttons
with Horizontal(id="button_container"): with Horizontal(id="button_container"):
self.refresh_button = Button("🔄 Refresh", id="refresh_btn") yield Button("Refresh", id="refresh_btn")
self.refresh_button.styles.width = "15%" yield Button("Select All", id="select_all_btn")
self.refresh_button.styles.margin = (1, 1, 1, 1) yield Button("Clear Selection", id="select_none_btn")
yield self.refresh_button yield Button("Revoke Selected", id="revoke_btn", variant="error")
self.select_all_button = Button("☑️ Select All", id="select_all_btn")
self.select_all_button.styles.width = "15%"
self.select_all_button.styles.margin = (1, 1, 1, 1)
yield self.select_all_button
self.select_none_button = Button(
"❌ Clear Selection", id="select_none_btn"
)
self.select_none_button.styles.width = "20%"
self.select_none_button.styles.margin = (1, 1, 1, 1)
yield self.select_none_button
self.revoke_button = Button(
"🛑 Revoke Selected", id="revoke_btn", variant="error"
)
self.revoke_button.styles.width = "20%"
self.revoke_button.styles.margin = (1, 1, 1, 1)
yield self.revoke_button
# Results display # Results display
with Vertical(id="result_container"): with Vertical(id="result_container"):
@@ -122,7 +108,7 @@ class OTPRevokeWidget(Static):
# Configure sessions table # Configure sessions table
self.sessions_table.clear() self.sessions_table.clear()
self.sessions_table.add_columns( self.sessions_table.add_columns(
"", "OTP ID", "Hostname", "Status", "Purpose", "Granted" "", "OTP ID", "Hostname", "Status", "Purpose", "Granted"
) )
# Enable row selection with checkbox column # Enable row selection with checkbox column
@@ -213,7 +199,11 @@ class OTPRevokeWidget(Static):
elif btn.id == "select_all_btn": elif btn.id == "select_all_btn":
# Select all visible rows # Select all visible rows
if self._filtered_df is not None: if (
self._filtered_df is not None
and not self._filtered_df.empty
and "otpid" in self._filtered_df.columns
):
self._selected_otpids = set(str(x) for x in self._filtered_df["otpid"]) self._selected_otpids = set(str(x) for x in self._filtered_df["otpid"])
await self._refresh_table() await self._refresh_table()
@@ -235,7 +225,12 @@ class OTPRevokeWidget(Static):
# Get the row index from the cursor row # Get the row index from the cursor row
row_index = self.sessions_table.cursor_row row_index = self.sessions_table.cursor_row
if self._filtered_df is not None and row_index < len(self._filtered_df): if (
self._filtered_df is not None
and not self._filtered_df.empty
and "otpid" in self._filtered_df.columns
and row_index < len(self._filtered_df)
):
# Get the OTP ID for this row # Get the OTP ID for this row
otpid = str(self._filtered_df.iloc[row_index]["otpid"]) otpid = str(self._filtered_df.iloc[row_index]["otpid"])
@@ -257,12 +252,12 @@ class OTPRevokeWidget(Static):
async def _revoke_selected(self) -> None: async def _revoke_selected(self) -> None:
"""Revoke the selected OTP sessions.""" """Revoke the selected OTP sessions."""
if not self._selected_otpids: if not self._selected_otpids:
self.results_display.update("No sessions selected for revocation") self.results_display.update("No sessions selected for revocation")
return return
api = getattr(self.app, "api", None) api = getattr(self.app, "api", None)
if not api: if not api:
self.results_display.update("API not available") self.results_display.update("API not available")
return return
# Collect results # Collect results
@@ -303,13 +298,13 @@ class OTPRevokeWidget(Static):
else "No response" else "No response"
) )
results.append( results.append(
f"Failed to revoke OTP {otpid} for {hostname}: {error_msg}" f"Failed to revoke OTP {otpid} for {hostname}: {error_msg}"
) )
logger.error(f"Failed to revoke OTP {otpid}: {error_msg}") logger.error(f"Failed to revoke OTP {otpid}: {error_msg}")
except Exception as e: except Exception as e:
failure_count += 1 failure_count += 1
results.append(f"Error revoking OTP {otpid}: {str(e)}") results.append(f"Error revoking OTP {otpid}: {str(e)}")
logger.exception(f"Exception revoking OTP {otpid}: {e}") logger.exception(f"Exception revoking OTP {otpid}: {e}")
# Update results display # Update results display
@@ -368,7 +363,11 @@ class OTPRevokeScreen(Screen):
async def action_select_all(self) -> None: async def action_select_all(self) -> None:
"""Select all visible sessions.""" """Select all visible sessions."""
if self.widget._filtered_df is not None: if (
self.widget._filtered_df is not None
and not self.widget._filtered_df.empty
and "otpid" in self.widget._filtered_df.columns
):
self.widget._selected_otpids = set( self.widget._selected_otpids = set(
str(x) for x in self.widget._filtered_df["otpid"] str(x) for x in self.widget._filtered_df["otpid"]
) )
+1 -1
View File
@@ -411,7 +411,7 @@ class QuietAgentWorkflowScreen(Screen):
# Delay the analysis start to ensure UI refresh completes first # Delay the analysis start to ensure UI refresh completes first
# This prevents Rust output from starting before the screen is cleared # This prevents Rust output from starting before the screen is cleared
self.set_timer(0.2, self._perform_analysis_worker) self.set_timer(0.5, self._perform_analysis_worker)
def _perform_analysis_worker(self) -> None: def _perform_analysis_worker(self) -> None:
"""Perform the actual agent activity analysis.""" """Perform the actual agent activity analysis."""
+4 -4
View File
@@ -92,15 +92,15 @@ class MainMenuScreen(Screen):
BUTTON_DEFS = { BUTTON_DEFS = {
"agent_actions": [ "agent_actions": [
( (
"🖥️ - Find, Move, or Generate OTP for Agents", "🖥️ - Find agent, Move agent, or Generate One Time Pass",
"move_agent_workflow_button", "move_agent_workflow_button",
), ),
("🎫 - Review and appove OTP Activities", "otp_activities_button"), ("🎫 - Review and approve OTP Activities", "otp_activities_button"),
("🔕 - Find and Move Quiet Hosts to Enforcement", "find_quiet_button"), ("🛑 - Revoke Active OTP Session", "otp_revoke_button"),
], ],
"policy": [ "policy": [
("⚖️ - Prepare Policy For Enforcement", "policy_prep_button"), ("⚖️ - Prepare Policy For Enforcement", "policy_prep_button"),
("🛑 - Revoke OTPs", "otp_revoke_button"), ("🔕 - Find and Move Quiet Hosts to Enforcement", "find_quiet_button"),
], ],
} }