# Copyright (C) 2025 James Brotosky, Brandon Wickline # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published # by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . import logging import os import platform from blessed import Terminal import dotenv from flows.otp import otp_activities_by_agent, otp_generate, otp_revoke from flows.prepPolicy import menu_policy_enforce from flows.quietAgent import findQuietAgents from services.agenthandler import ( findAgents, moveAgents, toggleEnforcement, ) from services.API import AirlockAPIWrapper from services.policyhandler import confirmUpdateAfromE from utils.configmanager import load_env logger = logging.getLogger(__name__) dotenv.load_dotenv() term = Terminal() logo = r""" _____ .__ .__ __ ___________ .__ / _ \ |__|______| | ____ ____ | | __ \__ ___/___ ____ | | ______ / /_\ \| \_ __ \ | / _ \_/ ___\| |/ / | | / _ \ / _ \| | / ___/ / | \ || | \/ |_( <_> ) \___| < | |( <_> | <_> ) |__\___ \ \____|__ /__||__| |____/\____/ \___ >__|_ \ |____| \____/ \____/|____/____ > \/ \/ \/ \/ """ footer_keys = ["F: Folder", "S: Settings", "B: Back", "Q: Quit"] # Box drawing characters TOP_LEFT = '╔' TOP_RIGHT = '╗' BOTTOM_LEFT = '╚' BOTTOM_RIGHT = '╝' HORIZONTAL = '═' VERTICAL = '║' SHADOW = '░' logo_lines = logo.splitlines() logo_width = max(len(line) for line in logo_lines) def draw_screen(title, items, selected_index): print(term.clear) center_x = (term.width - logo_width) // 2 top = len(logo_lines) + 2 height = len(items) + 4 for i, line in enumerate(logo_lines): print(term.move_xy(center_x, i) + term.cyan(line)) box_color = term.bright_magenta # or term.cyan, term.green, etc. print(term.move_xy(center_x, top) + box_color(TOP_LEFT + HORIZONTAL * logo_width + TOP_RIGHT)) for i in range(height): print(term.move_xy(center_x, top + 1 + i) + box_color(VERTICAL) + ' ' * logo_width + box_color(VERTICAL)) print(term.move_xy(center_x, top + 1 + height) + box_color(BOTTOM_LEFT + HORIZONTAL * logo_width + BOTTOM_RIGHT)) for i in range(1,height + 2): print(term.move_xy(center_x + logo_width + 2, top + i) + term.darkgray(SHADOW)) print(term.move_xy(center_x + 1, top + height + 2) + term.darkgray(SHADOW * (logo_width + 2))) print(term.move_xy(center_x + 4, top) + term.bold_magenta(title)) for i, item in enumerate(items): style = term.reverse if i == selected_index else term.bold_yellow print(term.move_xy(center_x + 4, top + 2 + i) + style(f"{i+1}. {item}")) footer_text = " ".join([term.bold_cyan(k) for k in footer_keys]) footer_x = (term.width - len(footer_text)) // 2 print(term.move_xy(footer_x, term.height - 2) + footer_text) print(term.move_xy(footer_x, term.height - 4) + term.bold("Use ↑/↓ or number keys. Press Enter to select."), end='', flush=True) def run_legacy_function(func, *args, **kwargs): try: # Exit fullscreen and restore terminal state print(term.exit_fullscreen, end='', flush=True) print(term.normal_cursor, end='', flush=True) # Reset terminal state on Linux if platform.system() != 'Windows': os.system('stty sane') # Clear screen os.system('cls' if os.name == 'nt' else 'clear') # Run the legacy function func(*args, **kwargs) input("Press Enter to return to the previous menu...") except Exception as e: print(f"Error during legacy function: {e}") input("Press Enter to return to the Main Menu...") def run_menu(api: AirlockAPIWrapper, start_menu="Main Menu"): current_menu = start_menu extras = load_env("EXTRAS") selected_index = 0 history = [] menus = { "Main Menu": [ "🔍 - Device Search", "🔀 - Move Device(s)", "🎫 - One Time Pass (OTP)", "🔇 - Find Quiet Hosts", ], "🔀 - Move Device(s)": [ "✅ - Move to local approval (Placeholder)", "🔄 - Move to Audit/Enforcement", "🔀 - Move - Other" ], "🎫 - One Time Pass (OTP)": [ "🔐 - Generate OTPs", "📊 - OTP Activities By Agent", "❌ - Revoke OTPs" ], "Settings": [ "(Placeholder) Change Working Directory" ] } if extras == "POLICYPREP": menus["Main Menu"].insert(4, "🛡️ - Policy Enforcement Tools") menus["🛡️ - Policy Enforcement Tools"] = [ "🔒 - Prepare Policy For Enforcement", "🔄 - Update Audit Policies" ] actions = { "🔍 - Device Search": (findAgents, [api, False]), "🔇 - Find Quiet Hosts": (findQuietAgents, [api]), "🔄 - Move to Audit/Enforcement": (toggleEnforcement, [api]), "🔀 - Move - Other": (moveAgents, [api]), "🔐 - Generate OTPs": (otp_generate, [api]), "📊 - OTP Activities By Agent": (otp_activities_by_agent, [api]), "❌ - Revoke OTPs": (otp_revoke, [api]), "🔄 - Update Audit Policies": (confirmUpdateAfromE, [api]), "🔒 - Prepare Policy For Enforcement": (menu_policy_enforce, [api]) } while True: func_to_run = None args_to_run = [] with term.fullscreen(), term.cbreak(), term.hidden_cursor(): draw_screen(current_menu, menus[current_menu], selected_index) key = term.inkey() items = menus[current_menu] if key.name == "KEY_UP": selected_index = (selected_index - 1) % len(items) elif key.name == "KEY_DOWN": selected_index = (selected_index + 1) % len(items) elif key.name == "KEY_ENTER" or key == "\n": selected_item = items[selected_index] if selected_item in menus: history.append(current_menu) current_menu = selected_item selected_index = 0 elif selected_item in actions: func_to_run, args_to_run = actions[selected_item] elif key.upper() == "Q": return elif key.upper() == "B": if history: current_menu = history.pop() selected_index = 0 elif key.upper() == "F": print(term.move_xy(4, term.height - 6) + term.bold_green("Folder selected")) term.inkey(timeout=2) elif key.upper() == "S": history.append(current_menu) # Add this line current_menu = "Settings" selected_index = 0 elif key.isdigit(): num = int(key) if 1 <= num <= len(items): selected_index = num - 1 selected_item = items[selected_index] if selected_item in menus: history.append(current_menu) current_menu = selected_item selected_index = 0 elif selected_item in actions: func_to_run, args_to_run = actions[selected_item] # Run legacy function outside of terminal context if func_to_run: run_legacy_function(func_to_run, *args_to_run)