58 lines
1.9 KiB
C#
58 lines
1.9 KiB
C#
using Spectre.Console;
|
|
using ZB.MOM.WW.Secrets.Abstractions;
|
|
|
|
namespace ZB.MOM.WW.Secrets.Cli.Interactive.Flows;
|
|
|
|
/// <summary>
|
|
/// Reveals a secret's decrypted plaintext on screen — the one flow whose purpose is to surface a value.
|
|
/// It gates the disclosure behind an explicit confirmation: on decline nothing is printed; on accept the
|
|
/// value is decrypted with the session cipher and written exactly once (no caching, no resolver). Requires
|
|
/// a KEK-capable session.
|
|
/// </summary>
|
|
public sealed class RevealSecretFlow : IInteractiveFlow
|
|
{
|
|
/// <inheritdoc />
|
|
public string Title => "Get (reveal) a secret";
|
|
|
|
/// <inheritdoc />
|
|
public bool RequiresKek => true;
|
|
|
|
/// <inheritdoc />
|
|
public async Task RunAsync(IAnsiConsole console, SecretsSession session, CancellationToken ct)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(console);
|
|
ArgumentNullException.ThrowIfNull(session);
|
|
|
|
var name = new SecretName(console.Prompt(
|
|
new TextPrompt<string>("Secret [green]name[/]:").Validate(NameValidation)));
|
|
|
|
StoredSecret? row = await session.Store.GetAsync(name, ct).ConfigureAwait(false);
|
|
if (row is null || row.IsDeleted)
|
|
{
|
|
console.MarkupLineInterpolated($"[red]No secret named '{name.Value}'.[/]");
|
|
return;
|
|
}
|
|
|
|
if (!console.Prompt(new ConfirmationPrompt("Reveal the value on screen?") { DefaultValue = false }))
|
|
{
|
|
return;
|
|
}
|
|
|
|
console.WriteLine(session.Cipher!.Decrypt(row));
|
|
}
|
|
|
|
// Re-prompts on an invalid name by surfacing the SecretName constructor's own diagnostic.
|
|
private static ValidationResult NameValidation(string raw)
|
|
{
|
|
try
|
|
{
|
|
_ = new SecretName(raw);
|
|
return ValidationResult.Success();
|
|
}
|
|
catch (ArgumentException ex)
|
|
{
|
|
return ValidationResult.Error(ex.Message);
|
|
}
|
|
}
|
|
}
|