// Copyright (C) 2023 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
using VaultSharp;
using VaultSharp.V1.Commons;
using VNLib.Utils.Memory;
using VNLib.Plugins.Essentials.Extensions;
namespace NVault.VaultExtensions
{
public static class VaultClientExtensions
{
private static string GetKeyPath(IVaultClientScope client, in VaultUserScope scope, string itemPath)
{
//Allow for null entry path
return client.EntryPath == null ? $"{scope.UserId}/{itemPath}" : $"{client.EntryPath}/{scope.UserId}/{itemPath}";
}
public static Task GetSecretAsync(this IVaultClient client, IVaultKvClientScope scope, VaultUserScope user, string path)
{
return GetSecretAsync(client, scope, user, path, scope.StorageProperty);
}
public static async Task GetSecretAsync(this IVaultClient client, IVaultClientScope scope, VaultUserScope user, string path, string property)
{
//Get the path complete path for the scope
string fullPath = GetKeyPath(scope, user, path);
//Get the secret from the vault
Secret result = await client.V1.Secrets.KeyValue.V2.ReadSecretAsync(fullPath, mountPoint:scope.MountPoint);
//Try to get the secret value from the store
string? value = result.Data.Data.GetValueOrDefault(property)?.ToString();
//Return the secret value as a private string
return value == null ? null : new PrivateString(value);
}
///
/// Writes a secret to the vault that is scoped by the vault scope, and the user scope.
///
///
/// The client scope configuration
/// The user scope to isolate the
/// The item path within the current scope
/// The secret value to set at the desired property
/// A task that resolves when the secret has been updated
public static async Task SetSecretAsync(this IVaultClient client, IVaultKvClientScope scope, VaultUserScope user, string path, PrivateString secret)
{
Dictionary secretDict = new()
{
//Dangerous cast, but we know the type
{ scope.StorageProperty, (string)secret }
};
//Await the result so we be sure the secret is not destroyed
return await SetSecretAsync(client, scope, user, path, secretDict);
}
///
/// Writes a secret to the vault that is scoped by the vault scope, and the user scope.
///
///
/// The client scope configuration
/// The user scope to isolate the
/// The item path within the current scope
/// The secret value to set at the desired property
/// A task that resolves when the secret has been updated
public static async Task SetSecretAsync(this IVaultClient client, IVaultClientScope scope, VaultUserScope user, string path, IDictionary secret)
{
//Get the path complete path for the scope
string fullPath = GetKeyPath(scope, user, path);
//Get the secret from the vault
Secret result = await client.V1.Secrets.KeyValue.V2.WriteSecretAsync(fullPath, secret, mountPoint:scope.MountPoint);
return result.Data;
}
///
/// Deletes a secret from the vault that is scoped by the vault scope, and the user scope.
///
///
/// The client scope
/// The vault user scope
/// The path to the storage
/// A task that resolves when the delete operation has completed
public static Task DeleteSecretAsync(this IVaultClient client, IVaultClientScope scope, VaultUserScope user, string path)
{
string fullApth = GetKeyPath(scope, user, path);
return client.V1.Secrets.KeyValue.V2.DeleteSecretAsync(fullApth, mountPoint:scope.MountPoint);
}
///
/// Deletes a secret from the vault
///
/// The user scope of the secret
/// The path to the secret
/// A token to cancel the operation
/// A task that returns when the operation has completed
public static Task DeleteSecretAsync(this IKvVaultStore store, VaultUserScope user, string path, CancellationToken cancellation)
{
return store.DeleteSecretAsync(user, path).WaitAsync(cancellation);
}
///
/// Gets a secret from the vault at the specified path and user scope
///
/// The user scope to get the value from
/// The secret path
/// A token to cancel the operation
/// A task that resolves the secret if found, null otherwise
public static Task GetSecretAsync(this IKvVaultStore store, VaultUserScope user, string path, CancellationToken cancellation)
{
return store.GetSecretAsync(user, path).WaitAsync(cancellation);
}
///
/// Sets a secret in the vault at the specified path and user scope
///
/// The user scope to store the value at
/// The path to the secret
/// The secret value to set
/// The cancellation token
/// A task that resolves when the secret has been updated
public static Task SetSecretAsync(this IKvVaultStore store, VaultUserScope user, string path, PrivateString secret, CancellationToken cancellation)
{
return store.SetSecretAsync(user, path, secret).WaitAsync(cancellation);
}
}
}