using HanaToolbox.Config; using HanaToolbox.Logging; using HanaToolbox.Services; using HanaToolbox.Services.Interfaces; using Spectre.Console; namespace HanaToolbox.Tui; /// /// Interactive TUI for managing hdbuserstore keys. /// Mirrors keymanager.sh flow: Create / Delete / Test. /// public sealed class KeyManagerTui( KeyManagerService keyService, IHdbClientLocator locator, AppLogger _logger) { public async Task RunAsync(HanaConfig hana, string sid, CancellationToken ct = default) { var hdbsql = locator.LocateHdbsql(hana.HdbsqlPath, sid, hana.InstanceNumber); while (true) { AnsiConsole.Clear(); AnsiConsole.Write(new Rule("[blue]SAP HANA Secure User Store Key Manager[/]").RuleStyle("blue")); AnsiConsole.WriteLine(); var choice = AnsiConsole.Prompt( new SelectionPrompt() .Title("Select an action:") .AddChoices("Create a New Key", "Delete an Existing Key", "Test an Existing Key", "Exit") .HighlightStyle("cyan")); switch (choice) { case "Create a New Key": await CreateKeyAsync(hdbsql, hana, sid, ct); break; case "Delete an Existing Key": await DeleteKeyAsync(sid, ct); break; case "Test an Existing Key": await TestKeyAsync(hdbsql, sid, ct); break; case "Exit": return; } AnsiConsole.MarkupLine("\n[grey]Press any key to continue...[/]"); Console.ReadKey(intercept: true); } } private async Task CreateKeyAsync( string hdbsql, HanaConfig hana, string sid, CancellationToken ct) { AnsiConsole.Write(new Rule("[blue]Create New Key[/]").RuleStyle("blue")); var keyName = AnsiConsole.Prompt(new TextPrompt("Key name:").DefaultValue("CRONKEY")); var host = AnsiConsole.Prompt(new TextPrompt("HANA host:").DefaultValue(System.Net.Dns.GetHostName())); var instance = AnsiConsole.Prompt(new TextPrompt("Instance number:").DefaultValue(hana.InstanceNumber)); var isSystemDb = AnsiConsole.Confirm("Connecting to SYSTEMDB?", defaultValue: false); string connStr; if (isSystemDb) { connStr = $"{host}:3{instance}13"; } else { var tenant = AnsiConsole.Prompt(new TextPrompt("Tenant DB name:").DefaultValue(sid.ToUpperInvariant())); connStr = $"{host}:3{instance}15@{tenant}"; } var user = AnsiConsole.Prompt(new TextPrompt("Database user:").DefaultValue("SYSTEM")); var pass = AnsiConsole.Prompt(new TextPrompt("Password:").Secret()); AnsiConsole.MarkupLine($"\n[yellow]Command preview:[/] hdbuserstore SET \"{keyName}\" \"{connStr}\" \"{user}\" "); if (!AnsiConsole.Confirm("Execute?", defaultValue: true)) return; var created = await keyService.CreateKeyAsync(keyName, connStr, user, pass, sid, ct); if (!created) return; // Auto-test and rollback on failure var ok = await keyService.TestKeyAsync(hdbsql, keyName, sid, ct); if (!ok) { AnsiConsole.MarkupLine("[yellow]Rolling back: deleting key due to connection failure...[/]"); await keyService.DeleteKeyAsync(keyName, sid, ct); } } private async Task DeleteKeyAsync(string sid, CancellationToken ct) { AnsiConsole.Write(new Rule("[red]Delete Key[/]").RuleStyle("red")); var keys = await keyService.ListKeysAsync(sid, ct); if (keys.Count == 0) { AnsiConsole.MarkupLine("[yellow]No keys found.[/]"); return; } var key = AnsiConsole.Prompt( new SelectionPrompt() .Title("Select key to delete:") .AddChoices(keys) .HighlightStyle("red")); if (!AnsiConsole.Confirm($"Permanently delete '{key}'?", defaultValue: false)) return; await keyService.DeleteKeyAsync(key, sid, ct); } private async Task TestKeyAsync(string hdbsql, string sid, CancellationToken ct) { AnsiConsole.Write(new Rule("[blue]Test Key[/]").RuleStyle("blue")); var keys = await keyService.ListKeysAsync(sid, ct); if (keys.Count == 0) { AnsiConsole.MarkupLine("[yellow]No keys found.[/]"); return; } var key = AnsiConsole.Prompt( new SelectionPrompt() .Title("Select key to test:") .AddChoices(keys) .HighlightStyle("cyan")); await keyService.TestKeyAsync(hdbsql, key, sid, ct); } }