RustImplementation #49
@@ -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"]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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
@@ -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"),
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user