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);
}
}