58 lines
2.4 KiB
C#
58 lines
2.4 KiB
C#
using Spectre.Console;
|
|
using ZB.MOM.WW.Secrets.Abstractions;
|
|
|
|
namespace ZB.MOM.WW.Secrets.Cli.Interactive.Flows;
|
|
|
|
/// <summary>
|
|
/// Creates or rotates a secret: prompts for a validated name, confirms before overwriting an existing
|
|
/// live row, reads the value through a masked prompt, and seals it under a fresh per-secret DEK via the
|
|
/// session cipher. The value is never echoed and the confirmation line carries the name only. Requires a
|
|
/// KEK-capable session (the shell upgrades a degraded session before dispatch).
|
|
/// </summary>
|
|
public sealed class SetSecretFlow : IInteractiveFlow
|
|
{
|
|
/// <inheritdoc />
|
|
public string Title => "Set / rotate a secret";
|
|
|
|
/// <inheritdoc />
|
|
public bool RequiresKek => true;
|
|
|
|
/// <inheritdoc />
|
|
public async Task RunAsync(IAnsiConsole console, SecretsSession session, CancellationToken ct)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(console);
|
|
ArgumentNullException.ThrowIfNull(session);
|
|
|
|
ISecretCipher cipher = session.Cipher
|
|
?? throw new InvalidOperationException("flow requires an unlocked KEK session");
|
|
|
|
var name = new SecretName(console.Prompt(
|
|
new TextPrompt<string>("Secret [green]name[/]:").Validate(FlowPrompts.ValidateSecretName)));
|
|
|
|
StoredSecret? existing = await session.Store.GetAsync(name, ct).ConfigureAwait(false);
|
|
if (existing is { IsDeleted: false } &&
|
|
!console.Prompt(new ConfirmationPrompt($"'{name.Value}' exists — overwrite in place?") { DefaultValue = false }))
|
|
{
|
|
console.MarkupLine("[yellow]Left unchanged.[/]");
|
|
return;
|
|
}
|
|
|
|
string value = console.Prompt(new TextPrompt<string>("Secret [green]value[/]:").Secret());
|
|
SecretContentType contentType = console.Prompt(
|
|
new SelectionPrompt<SecretContentType>().Title("Content type").AddChoices(Enum.GetValues<SecretContentType>()));
|
|
string description = console.Prompt(
|
|
new TextPrompt<string>("Description [grey](optional)[/]:").AllowEmpty());
|
|
|
|
string actor = FlowPrompts.Actor;
|
|
StoredSecret row = cipher.Encrypt(name, value, contentType) with
|
|
{
|
|
Description = string.IsNullOrWhiteSpace(description) ? null : description,
|
|
CreatedBy = actor,
|
|
UpdatedBy = actor,
|
|
};
|
|
await session.Store.UpsertAsync(row, ct).ConfigureAwait(false);
|
|
|
|
console.MarkupLineInterpolated($"[green]Sealed '{name.Value}'.[/]");
|
|
}
|
|
}
|