XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEWorkflow

【WinUI】XFE基于windows的工作流

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using XFEWorkflow.Abstractions;

namespace XFEWorkflow.Infrastructure.Storage;

public sealed class CredentialVault(ClientState state)
{
    private static readonly byte[] Entropy = Encoding.UTF8.GetBytes("XFEWorkflow.Credentials.v1");
    public List<string> References => state.Store.Read(p => p.InstanceCredentials.Keys.Order().ToList());
    public async Task SetAsync(string reference, string secret, CancellationToken ct = default)
    {
        if (!OperatingSystem.IsWindows()) throw new PlatformNotSupportedException("凭据存储需要 Windows");
        if (string.IsNullOrWhiteSpace(reference) || string.IsNullOrWhiteSpace(secret)) throw new ArgumentException("名称和凭据不能为空");
        var encrypted = Convert.ToBase64String(ProtectedData.Protect(Encoding.UTF8.GetBytes(secret), Entropy, DataProtectionScope.CurrentUser));
        state.Store.Update(p => p.InstanceCredentials[reference.Trim()] = encrypted);
        await state.SaveAsync(ct);
    }
    public string Get(string reference)
    {
        if (!OperatingSystem.IsWindows()) throw new PlatformNotSupportedException("凭据存储需要 Windows");
        var encrypted = state.Store.Read(p => p.InstanceCredentials.GetValueOrDefault(reference)) ?? throw new KeyNotFoundException("凭据不存在:" + reference);
        return Encoding.UTF8.GetString(ProtectedData.Unprotect(Convert.FromBase64String(encrypted), Entropy, DataProtectionScope.CurrentUser));
    }
    public async Task RemoveAsync(string reference)
    {
        state.Store.Update(p => p.InstanceCredentials.Remove(reference));
        await state.SaveAsync();
    }
}

public sealed class SecretRedactor
{
    private readonly HashSet<string> secrets = [];
    private readonly object gate = new();
    public void Add(string value)
    {
        if (value.Length == 0) return;
        var readable = JsonValue.Create(value).Display();
        lock (gate)
        {
            secrets.Add(value);
            secrets.Add(JsonEncodedText.Encode(value).ToString());
            secrets.Add(readable[1..^1]);
        }
    }
    public string Redact(string value)
    {
        lock (gate)
            foreach (var secret in secrets.OrderByDescending(x => x.Length))
                value = value.Replace(secret, "***", StringComparison.Ordinal);
        return value;
    }
}