Files
HanaToolbox/Tui/KeyManagerTui.cs
2026-03-02 20:53:28 +01:00

135 lines
4.9 KiB
C#

using HanaToolbox.Config;
using HanaToolbox.Logging;
using HanaToolbox.Services;
using HanaToolbox.Services.Interfaces;
using Spectre.Console;
namespace HanaToolbox.Tui;
/// <summary>
/// Interactive TUI for managing hdbuserstore keys.
/// Mirrors keymanager.sh flow: Create / Delete / Test.
/// </summary>
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<string>()
.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<string>("Key name:").DefaultValue("CRONKEY"));
var host = AnsiConsole.Prompt(new TextPrompt<string>("HANA host:").DefaultValue(System.Net.Dns.GetHostName()));
var instance = AnsiConsole.Prompt(new TextPrompt<string>("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<string>("Tenant DB name:").DefaultValue(sid.ToUpperInvariant()));
connStr = $"{host}:3{instance}15@{tenant}";
}
var user = AnsiConsole.Prompt(new TextPrompt<string>("Database user:").DefaultValue("SYSTEM"));
var pass = AnsiConsole.Prompt(new TextPrompt<string>("Password:").Secret());
AnsiConsole.MarkupLine($"\n[yellow]Command preview:[/] hdbuserstore SET \"{keyName}\" \"{connStr}\" \"{user}\" <password>");
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<string>()
.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<string>()
.Title("Select key to test:")
.AddChoices(keys)
.HighlightStyle("cyan"));
await keyService.TestKeyAsync(hdbsql, key, sid, ct);
}
}