mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-05-21 15:12:16 +00:00
Refactor secret management to include SecretStoreType for data sources
This commit is contained in:
parent
e9fa4187fc
commit
cea7f09653
@ -116,7 +116,7 @@ public partial class DataSourceERI_V1Dialog : MSGComponentBase, ISecretId
|
||||
if (this.dataAuthMethod is AuthMethod.TOKEN or AuthMethod.USERNAME_PASSWORD)
|
||||
{
|
||||
// Load the secret:
|
||||
var requestedSecret = await this.RustService.GetSecret(this);
|
||||
var requestedSecret = await this.RustService.GetSecret(this, SecretStoreType.DATA_SOURCE);
|
||||
if (requestedSecret.Success)
|
||||
this.dataSecret = await requestedSecret.Secret.Decrypt(this.encryption);
|
||||
else
|
||||
@ -324,7 +324,7 @@ public partial class DataSourceERI_V1Dialog : MSGComponentBase, ISecretId
|
||||
if (!string.IsNullOrWhiteSpace(this.dataSecret))
|
||||
{
|
||||
// Store the secret in the OS secure storage:
|
||||
var storeResponse = await this.RustService.SetSecret(this, this.dataSecret);
|
||||
var storeResponse = await this.RustService.SetSecret(this, this.dataSecret, SecretStoreType.DATA_SOURCE);
|
||||
if (!storeResponse.Success)
|
||||
{
|
||||
this.dataSecretStorageIssue = string.Format(T("Failed to store the auth. secret in the operating system. The message was: {0}. Please try again."), storeResponse.Issue);
|
||||
|
||||
@ -180,7 +180,7 @@ public partial class SettingsDialogDataSources : SettingsDialogBase
|
||||
// All other auth methods require a secret, which we need to delete now:
|
||||
else
|
||||
{
|
||||
var deleteSecretResponse = await this.RustService.DeleteSecret(externalDataSource);
|
||||
var deleteSecretResponse = await this.RustService.DeleteSecret(externalDataSource, SecretStoreType.DATA_SOURCE);
|
||||
if (deleteSecretResponse.Success)
|
||||
applyChanges = true;
|
||||
}
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
|
||||
using AIStudio.Assistants.ERI;
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Tools;
|
||||
using AIStudio.Tools.ERIClient;
|
||||
using AIStudio.Tools.ERIClient.DataModel;
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
@ -313,7 +312,8 @@ public readonly record struct DataSourceERI_V1 : IERIDataSource
|
||||
PendingEnterpriseSecrets.Add(new(
|
||||
$"{ISecretId.ENTERPRISE_KEY_PREFIX}::{dataSource.Id}",
|
||||
dataSource.Name,
|
||||
decryptedSecret));
|
||||
decryptedSecret,
|
||||
SecretStoreType.DATA_SOURCE));
|
||||
LOGGER.LogDebug($"Successfully decrypted the {secretFieldName} for data source {idx}. It will be stored in the OS keyring. (Plugin ID: {configPluginId})");
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -16,21 +16,6 @@ namespace AIStudio.Settings;
|
||||
[JsonDerivedType(typeof(DataSourceERI_V1), nameof(DataSourceType.ERI_V1))]
|
||||
public interface IDataSource : IConfigurationObject
|
||||
{
|
||||
/// <summary>
|
||||
/// The number of the data source.
|
||||
/// </summary>
|
||||
public uint Num { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The unique identifier of the data source.
|
||||
/// </summary>
|
||||
public string Id { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the data source.
|
||||
/// </summary>
|
||||
public string Name { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Which type of data source is this?
|
||||
/// </summary>
|
||||
@ -40,16 +25,6 @@ public interface IDataSource : IConfigurationObject
|
||||
/// Which data security policy is applied to this data source?
|
||||
/// </summary>
|
||||
public DataSourceSecurity SecurityPolicy { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Is this data source an enterprise configuration?
|
||||
/// </summary>
|
||||
public bool IsEnterpriseConfiguration { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the enterprise configuration plugin.
|
||||
/// </summary>
|
||||
public Guid EnterpriseConfigurationPluginId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The maximum number of matches to return when retrieving data from the ERI server.
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using AIStudio.Assistants.ERI;
|
||||
using AIStudio.Settings.DataModel;
|
||||
using AIStudio.Tools.ERIClient.DataModel;
|
||||
|
||||
namespace AIStudio.Settings;
|
||||
|
||||
@ -7,7 +7,7 @@ public interface IExternalDataSource : IDataSource, ISecretId
|
||||
#region Implementation of ISecretId
|
||||
|
||||
[JsonIgnore]
|
||||
string ISecretId.SecretId => this.IsEnterpriseConfiguration ? $"{ISecretId.ENTERPRISE_KEY_PREFIX}::{this.Id}" : this.Id;
|
||||
string ISecretId.SecretId => this.IsEnterpriseConfiguration ? $"{ENTERPRISE_KEY_PREFIX}::{this.Id}" : this.Id;
|
||||
|
||||
[JsonIgnore]
|
||||
string ISecretId.SecretName => this.Name;
|
||||
|
||||
@ -119,7 +119,7 @@ public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource),
|
||||
string password;
|
||||
if (string.IsNullOrWhiteSpace(temporarySecret))
|
||||
{
|
||||
var passwordResponse = await rustService.GetSecret(this.DataSource);
|
||||
var passwordResponse = await rustService.GetSecret(this.DataSource, SecretStoreType.DATA_SOURCE);
|
||||
if (!passwordResponse.Success)
|
||||
{
|
||||
return new()
|
||||
@ -173,7 +173,7 @@ public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource),
|
||||
string token;
|
||||
if (string.IsNullOrWhiteSpace(temporarySecret))
|
||||
{
|
||||
var tokenResponse = await rustService.GetSecret(this.DataSource);
|
||||
var tokenResponse = await rustService.GetSecret(this.DataSource, SecretStoreType.DATA_SOURCE);
|
||||
if (!tokenResponse.Success)
|
||||
{
|
||||
return new()
|
||||
|
||||
@ -13,81 +13,4 @@ public sealed record PendingEnterpriseApiKey(
|
||||
string SecretId,
|
||||
string SecretName,
|
||||
string ApiKey,
|
||||
SecretStoreType StoreType);
|
||||
|
||||
/// <summary>
|
||||
/// Static container for pending API keys during plugin loading.
|
||||
/// </summary>
|
||||
public static class PendingEnterpriseApiKeys
|
||||
{
|
||||
private static readonly List<PendingEnterpriseApiKey> PENDING_KEYS = [];
|
||||
private static readonly Lock LOCK = new();
|
||||
|
||||
/// <summary>
|
||||
/// Adds a pending API key to the list.
|
||||
/// </summary>
|
||||
/// <param name="key">The pending API key to add.</param>
|
||||
public static void Add(PendingEnterpriseApiKey key)
|
||||
{
|
||||
lock (LOCK)
|
||||
PENDING_KEYS.Add(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets and clears all pending API keys.
|
||||
/// </summary>
|
||||
/// <returns>A list of all pending API keys.</returns>
|
||||
public static IReadOnlyList<PendingEnterpriseApiKey> GetAndClear()
|
||||
{
|
||||
lock (LOCK)
|
||||
{
|
||||
var keys = PENDING_KEYS.ToList();
|
||||
PENDING_KEYS.Clear();
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a pending enterprise secret that needs to be stored in the OS keyring.
|
||||
/// </summary>
|
||||
/// <param name="SecretId">The secret ID.</param>
|
||||
/// <param name="SecretName">The secret name.</param>
|
||||
/// <param name="SecretData">The decrypted secret data.</param>
|
||||
public sealed record PendingEnterpriseSecret(
|
||||
string SecretId,
|
||||
string SecretName,
|
||||
string SecretData);
|
||||
|
||||
/// <summary>
|
||||
/// Static container for pending enterprise secrets during plugin loading.
|
||||
/// </summary>
|
||||
public static class PendingEnterpriseSecrets
|
||||
{
|
||||
private static readonly List<PendingEnterpriseSecret> PENDING_SECRETS = [];
|
||||
private static readonly Lock LOCK = new();
|
||||
|
||||
/// <summary>
|
||||
/// Adds a pending enterprise secret to the list.
|
||||
/// </summary>
|
||||
/// <param name="secret">The pending enterprise secret to add.</param>
|
||||
public static void Add(PendingEnterpriseSecret secret)
|
||||
{
|
||||
lock (LOCK)
|
||||
PENDING_SECRETS.Add(secret);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets and clears all pending enterprise secrets.
|
||||
/// </summary>
|
||||
/// <returns>A list of all pending enterprise secrets.</returns>
|
||||
public static IReadOnlyList<PendingEnterpriseSecret> GetAndClear()
|
||||
{
|
||||
lock (LOCK)
|
||||
{
|
||||
var secrets = PENDING_SECRETS.ToList();
|
||||
PENDING_SECRETS.Clear();
|
||||
return secrets;
|
||||
}
|
||||
}
|
||||
}
|
||||
SecretStoreType StoreType);
|
||||
@ -0,0 +1,34 @@
|
||||
namespace AIStudio.Tools.PluginSystem;
|
||||
|
||||
/// <summary>
|
||||
/// Static container for pending API keys during plugin loading.
|
||||
/// </summary>
|
||||
public static class PendingEnterpriseApiKeys
|
||||
{
|
||||
private static readonly List<PendingEnterpriseApiKey> PENDING_KEYS = [];
|
||||
private static readonly Lock LOCK = new();
|
||||
|
||||
/// <summary>
|
||||
/// Adds a pending API key to the list.
|
||||
/// </summary>
|
||||
/// <param name="key">The pending API key to add.</param>
|
||||
public static void Add(PendingEnterpriseApiKey key)
|
||||
{
|
||||
lock (LOCK)
|
||||
PENDING_KEYS.Add(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets and clears all pending API keys.
|
||||
/// </summary>
|
||||
/// <returns>A list of all pending API keys.</returns>
|
||||
public static IReadOnlyList<PendingEnterpriseApiKey> GetAndClear()
|
||||
{
|
||||
lock (LOCK)
|
||||
{
|
||||
var keys = PENDING_KEYS.ToList();
|
||||
PENDING_KEYS.Clear();
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
namespace AIStudio.Tools.PluginSystem;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a pending enterprise secret that needs to be stored in the OS keyring.
|
||||
/// </summary>
|
||||
/// <param name="SecretId">The secret ID.</param>
|
||||
/// <param name="SecretName">The secret name.</param>
|
||||
/// <param name="SecretData">The decrypted secret data.</param>
|
||||
/// <param name="StoreType">The type of secret store to use.</param>
|
||||
public sealed record PendingEnterpriseSecret(
|
||||
string SecretId,
|
||||
string SecretName,
|
||||
string SecretData,
|
||||
SecretStoreType StoreType);
|
||||
@ -0,0 +1,34 @@
|
||||
namespace AIStudio.Tools.PluginSystem;
|
||||
|
||||
/// <summary>
|
||||
/// Static container for pending enterprise secrets during plugin loading.
|
||||
/// </summary>
|
||||
public static class PendingEnterpriseSecrets
|
||||
{
|
||||
private static readonly List<PendingEnterpriseSecret> PENDING_SECRETS = [];
|
||||
private static readonly Lock LOCK = new();
|
||||
|
||||
/// <summary>
|
||||
/// Adds a pending enterprise secret to the list.
|
||||
/// </summary>
|
||||
/// <param name="secret">The pending enterprise secret to add.</param>
|
||||
public static void Add(PendingEnterpriseSecret secret)
|
||||
{
|
||||
lock (LOCK)
|
||||
PENDING_SECRETS.Add(secret);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets and clears all pending enterprise secrets.
|
||||
/// </summary>
|
||||
/// <returns>A list of all pending enterprise secrets.</returns>
|
||||
public static IReadOnlyList<PendingEnterpriseSecret> GetAndClear()
|
||||
{
|
||||
lock (LOCK)
|
||||
{
|
||||
var secrets = PENDING_SECRETS.ToList();
|
||||
PENDING_SECRETS.Clear();
|
||||
return secrets;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -62,7 +62,7 @@ public sealed class PluginConfiguration(bool isInternal, LuaState state, PluginT
|
||||
try
|
||||
{
|
||||
var secretId = new TemporarySecretId(pendingSecret.SecretId, pendingSecret.SecretName);
|
||||
var result = await rustService.SetSecret(secretId, pendingSecret.SecretData);
|
||||
var result = await rustService.SetSecret(secretId, pendingSecret.SecretData, pendingSecret.StoreType);
|
||||
|
||||
if (result.Success)
|
||||
LOG.LogDebug($"Successfully stored enterprise secret for '{pendingSecret.SecretName}' in the OS keyring.");
|
||||
|
||||
@ -252,6 +252,7 @@ public sealed record PluginConfigurationObject
|
||||
/// <param name="availablePlugins">A list of currently available plugins.</param>
|
||||
/// <param name="configObjectList">A list of all existing configuration objects.</param>
|
||||
/// <param name="secretStoreType">An optional parameter specifying the type of secret store to use for deleting associated API keys from the OS keyring, if applicable.</param>
|
||||
/// <param name="deleteSecret">When true, delete the associated non-API-key secret from the OS keyring.</param>
|
||||
/// <returns>Returns true if the configuration was altered during cleanup; otherwise, false.</returns>
|
||||
public static async Task<bool> CleanLeftOverConfigurationObjects<TClass>(
|
||||
PluginConfigurationObjectType configObjectType,
|
||||
@ -304,7 +305,7 @@ public sealed record PluginConfigurationObject
|
||||
// Delete the API key from the OS keyring if the removed object has one:
|
||||
if(deleteSecret && item is ISecretId regularSecretId)
|
||||
{
|
||||
var deleteResult = await RUST_SERVICE.DeleteSecret(regularSecretId);
|
||||
var deleteResult = await RUST_SERVICE.DeleteSecret(regularSecretId, secretStoreType ?? SecretStoreType.DATA_SOURCE);
|
||||
if (deleteResult.Success)
|
||||
LOG.LogInformation($"Successfully deleted secret for removed enterprise object '{item.Name}' from the OS keyring.");
|
||||
else
|
||||
|
||||
@ -175,7 +175,7 @@ public static partial class PluginFactory
|
||||
wasConfigurationChanged = true;
|
||||
|
||||
// Check data sources:
|
||||
if(await PluginConfigurationObject.CleanLeftOverConfigurationObjects(PluginConfigurationObjectType.DATA_SOURCE, x => x.DataSources, AVAILABLE_PLUGINS, configObjectList, deleteSecret: true))
|
||||
if(await PluginConfigurationObject.CleanLeftOverConfigurationObjects(PluginConfigurationObjectType.DATA_SOURCE, x => x.DataSources, AVAILABLE_PLUGINS, configObjectList, SecretStoreType.DATA_SOURCE, deleteSecret: true))
|
||||
wasConfigurationChanged = true;
|
||||
|
||||
// Check chat templates:
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
namespace AIStudio.Tools;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the type of secret store used for API keys.
|
||||
/// Represents the type of secret store used for API keys and other secrets.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Different provider types use different prefixes for storing API keys.
|
||||
/// Different provider and secret types use different prefixes for storing secrets.
|
||||
/// This prevents collisions when the same instance name is used across
|
||||
/// different provider types (e.g., LLM, Embedding, Transcription).
|
||||
/// </remarks>
|
||||
@ -29,4 +29,9 @@ public enum SecretStoreType
|
||||
/// Image provider secrets. Uses the "image::" prefix.
|
||||
/// </summary>
|
||||
IMAGE_PROVIDER,
|
||||
|
||||
/// <summary>
|
||||
/// Data source secrets. Uses the "data-source::" prefix.
|
||||
/// </summary>
|
||||
DATA_SOURCE,
|
||||
}
|
||||
@ -9,12 +9,14 @@ public static class SecretStoreTypeExtensions
|
||||
/// LLM_PROVIDER uses the legacy "provider" prefix for backward compatibility.
|
||||
/// </remarks>
|
||||
/// <param name="type">The SecretStoreType enum value.</param>
|
||||
/// <returns>>The corresponding prefix string.</returns>
|
||||
/// <returns>The corresponding prefix string.</returns>
|
||||
public static string Prefix(this SecretStoreType type) => type switch
|
||||
{
|
||||
SecretStoreType.LLM_PROVIDER => "provider",
|
||||
SecretStoreType.EMBEDDING_PROVIDER => "embedding",
|
||||
SecretStoreType.TRANSCRIPTION_PROVIDER => "transcription",
|
||||
SecretStoreType.IMAGE_PROVIDER => "image",
|
||||
SecretStoreType.DATA_SOURCE => "data-source",
|
||||
|
||||
_ => "provider",
|
||||
};
|
||||
|
||||
@ -200,7 +200,7 @@ public sealed class EnterpriseEnvironmentService(ILogger<EnterpriseEnvironmentSe
|
||||
{
|
||||
logger.LogInformation("The enterprise encryption secret changed. Refreshing the enterprise encryption service and reloading plugins.");
|
||||
PluginFactory.InitializeEnterpriseEncryption(enterpriseEncryptionSecret);
|
||||
await this.RemoveEnterpriseManagedApiKeysAsync();
|
||||
await this.RemoveEnterpriseManagedSecretsAsync();
|
||||
await PluginFactory.LoadAll();
|
||||
}
|
||||
|
||||
@ -249,36 +249,36 @@ public sealed class EnterpriseEnvironmentService(ILogger<EnterpriseEnvironmentSe
|
||||
return serverUrl.Trim().TrimEnd('/');
|
||||
}
|
||||
|
||||
private async Task RemoveEnterpriseManagedApiKeysAsync()
|
||||
private async Task RemoveEnterpriseManagedSecretsAsync()
|
||||
{
|
||||
var secretTargets = GetEnterpriseManagedSecretTargets();
|
||||
if (secretTargets.Count == 0)
|
||||
{
|
||||
logger.LogInformation("No enterprise-managed API keys are currently known in the settings. No keyring cleanup is required.");
|
||||
logger.LogInformation("No enterprise-managed secrets are currently known in the settings. No keyring cleanup is required.");
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogInformation("Removing {SecretCount} enterprise-managed API key(s) from the OS keyring after an enterprise encryption secret change.", secretTargets.Count);
|
||||
logger.LogInformation("Removing {SecretCount} enterprise-managed secret(s) from the OS keyring after an enterprise encryption secret change.", secretTargets.Count);
|
||||
foreach (var target in secretTargets)
|
||||
{
|
||||
try
|
||||
{
|
||||
var deleteResult = target.StoreType is { } storeType
|
||||
? await rustService.DeleteAPIKey(target, storeType)
|
||||
: await rustService.DeleteSecret(target);
|
||||
var deleteResult = target.StoreType is SecretStoreType.DATA_SOURCE
|
||||
? await rustService.DeleteSecret(target, target.StoreType)
|
||||
: await rustService.DeleteAPIKey(target, target.StoreType);
|
||||
if (deleteResult.Success)
|
||||
{
|
||||
if (deleteResult.WasEntryFound)
|
||||
logger.LogInformation("Successfully deleted enterprise-managed API key '{SecretName}' from the OS keyring.", target.SecretName);
|
||||
logger.LogInformation("Successfully deleted enterprise-managed secret '{SecretName}' from the OS keyring.", target.SecretName);
|
||||
else
|
||||
logger.LogInformation("Enterprise-managed API key '{SecretName}' was already absent from the OS keyring.", target.SecretName);
|
||||
logger.LogInformation("Enterprise-managed secret '{SecretName}' was already absent from the OS keyring.", target.SecretName);
|
||||
}
|
||||
else
|
||||
logger.LogWarning("Failed to delete enterprise-managed API key '{SecretName}' from the OS keyring: {Issue}", target.SecretName, deleteResult.Issue);
|
||||
logger.LogWarning("Failed to delete enterprise-managed secret '{SecretName}' from the OS keyring: {Issue}", target.SecretName, deleteResult.Issue);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogWarning(e, "Failed to delete enterprise-managed API key '{SecretName}' from the OS keyring.", target.SecretName);
|
||||
logger.LogWarning(e, "Failed to delete enterprise-managed secret '{SecretName}' from the OS keyring.", target.SecretName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -291,14 +291,14 @@ public sealed class EnterpriseEnvironmentService(ILogger<EnterpriseEnvironmentSe
|
||||
AddEnterpriseManagedSecretTargets(configurationData.Providers, SecretStoreType.LLM_PROVIDER, secretTargets);
|
||||
AddEnterpriseManagedSecretTargets(configurationData.EmbeddingProviders, SecretStoreType.EMBEDDING_PROVIDER, secretTargets);
|
||||
AddEnterpriseManagedSecretTargets(configurationData.TranscriptionProviders, SecretStoreType.TRANSCRIPTION_PROVIDER, secretTargets);
|
||||
AddEnterpriseManagedSecretTargets(configurationData.DataSources.OfType<IExternalDataSource>(), null, secretTargets);
|
||||
AddEnterpriseManagedSecretTargets(configurationData.DataSources.OfType<IExternalDataSource>(), SecretStoreType.DATA_SOURCE, secretTargets);
|
||||
|
||||
return secretTargets.ToList();
|
||||
}
|
||||
|
||||
private static void AddEnterpriseManagedSecretTargets<TSecret>(
|
||||
IEnumerable<TSecret> secrets,
|
||||
SecretStoreType? storeType,
|
||||
SecretStoreType storeType,
|
||||
ISet<EnterpriseSecretTarget> secretTargets) where TSecret : ISecretId, IConfigurationObject
|
||||
{
|
||||
foreach (var secret in secrets)
|
||||
|
||||
@ -4,26 +4,42 @@ namespace AIStudio.Tools.Services;
|
||||
|
||||
public sealed partial class RustService
|
||||
{
|
||||
private static string SecretKey(ISecretId secretId, SecretStoreType storeType) => $"{storeType.Prefix()}::{secretId.SecretId}::{secretId.SecretName}";
|
||||
|
||||
private static string LegacySecretKey(ISecretId secretId) => $"secret::{secretId.SecretId}::{secretId.SecretName}";
|
||||
|
||||
/// <summary>
|
||||
/// Try to get the secret data for the given secret ID.
|
||||
/// </summary>
|
||||
/// <param name="secretId">The secret ID to get the data for.</param>
|
||||
/// <param name="storeType">The secret store type.</param>
|
||||
/// <param name="isTrying">Indicates if we are trying to get the data. In that case, we don't log errors.</param>
|
||||
/// <returns>The requested secret.</returns>
|
||||
public async Task<RequestedSecret> GetSecret(ISecretId secretId, bool isTrying = false)
|
||||
public async Task<RequestedSecret> GetSecret(ISecretId secretId, SecretStoreType storeType, bool isTrying = false)
|
||||
{
|
||||
var secretRequest = new SelectSecretRequest($"secret::{secretId.SecretId}::{secretId.SecretName}", Environment.UserName, isTrying);
|
||||
var secretKey = SecretKey(secretId, storeType);
|
||||
var secretRequest = new SelectSecretRequest(secretKey, Environment.UserName, isTrying);
|
||||
var result = await this.http.PostAsJsonAsync("/secrets/get", secretRequest, this.jsonRustSerializerOptions);
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
if(!isTrying)
|
||||
this.logger!.LogError($"Failed to get the secret data for secret ID '{secretId.SecretId}' due to an API issue: '{result.StatusCode}'");
|
||||
this.logger!.LogError($"Failed to get the secret data for '{secretKey}' due to an API issue: '{result.StatusCode}'");
|
||||
return new RequestedSecret(false, new EncryptedText(string.Empty), TB("Failed to get the secret data due to an API issue."));
|
||||
}
|
||||
|
||||
var secret = await result.Content.ReadFromJsonAsync<RequestedSecret>(this.jsonRustSerializerOptions);
|
||||
if (!secret.Success && storeType is SecretStoreType.DATA_SOURCE)
|
||||
{
|
||||
var legacySecret = await this.GetLegacySecret(secretId, isTrying: true);
|
||||
if (legacySecret.Success)
|
||||
{
|
||||
this.logger!.LogDebug($"Successfully retrieved the legacy data source secret for '{LegacySecretKey(secretId)}'.");
|
||||
return legacySecret;
|
||||
}
|
||||
}
|
||||
|
||||
if (!secret.Success && !isTrying)
|
||||
this.logger!.LogError($"Failed to get the secret data for secret ID '{secretId.SecretId}': '{secret.Issue}'");
|
||||
this.logger!.LogError($"Failed to get the secret data for '{secretKey}': '{secret.Issue}'");
|
||||
|
||||
return secret;
|
||||
}
|
||||
@ -33,21 +49,26 @@ public sealed partial class RustService
|
||||
/// </summary>
|
||||
/// <param name="secretId">The secret ID to store the data for.</param>
|
||||
/// <param name="secretData">The data to store.</param>
|
||||
/// <param name="storeType">The secret store type.</param>
|
||||
/// <returns>The store secret response.</returns>
|
||||
public async Task<StoreSecretResponse> SetSecret(ISecretId secretId, string secretData)
|
||||
public async Task<StoreSecretResponse> SetSecret(ISecretId secretId, string secretData, SecretStoreType storeType)
|
||||
{
|
||||
var secretKey = SecretKey(secretId, storeType);
|
||||
var encryptedSecret = await this.encryptor!.Encrypt(secretData);
|
||||
var request = new StoreSecretRequest($"secret::{secretId.SecretId}::{secretId.SecretName}", Environment.UserName, encryptedSecret);
|
||||
var request = new StoreSecretRequest(secretKey, Environment.UserName, encryptedSecret);
|
||||
var result = await this.http.PostAsJsonAsync("/secrets/store", request, this.jsonRustSerializerOptions);
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
this.logger!.LogError($"Failed to store the secret data for secret ID '{secretId.SecretId}' due to an API issue: '{result.StatusCode}'");
|
||||
return new StoreSecretResponse(false, TB("Failed to get the secret data due to an API issue."));
|
||||
this.logger!.LogError($"Failed to store the secret data for '{secretKey}' due to an API issue: '{result.StatusCode}'");
|
||||
return new StoreSecretResponse(false, TB("Failed to store the secret data due to an API issue."));
|
||||
}
|
||||
|
||||
var state = await result.Content.ReadFromJsonAsync<StoreSecretResponse>(this.jsonRustSerializerOptions);
|
||||
if (!state.Success)
|
||||
this.logger!.LogError($"Failed to store the secret data for secret ID '{secretId.SecretId}': '{state.Issue}'");
|
||||
this.logger!.LogError($"Failed to store the secret data for '{secretKey}': '{state.Issue}'");
|
||||
|
||||
if (state.Success && storeType is SecretStoreType.DATA_SOURCE)
|
||||
await this.DeleteSecretKey(secretId, LegacySecretKey(secretId));
|
||||
|
||||
return state;
|
||||
}
|
||||
@ -56,20 +77,49 @@ public sealed partial class RustService
|
||||
/// Tries to delete the secret data for the given secret ID.
|
||||
/// </summary>
|
||||
/// <param name="secretId">The secret ID to delete the data for.</param>
|
||||
/// <param name="storeType">The secret store type.</param>
|
||||
/// <returns>The delete secret response.</returns>
|
||||
public async Task<DeleteSecretResponse> DeleteSecret(ISecretId secretId)
|
||||
public async Task<DeleteSecretResponse> DeleteSecret(ISecretId secretId, SecretStoreType storeType)
|
||||
{
|
||||
var request = new SelectSecretRequest($"secret::{secretId.SecretId}::{secretId.SecretName}", Environment.UserName, false);
|
||||
var deleteResult = await this.DeleteSecretKey(secretId, SecretKey(secretId, storeType));
|
||||
if (storeType is not SecretStoreType.DATA_SOURCE || !deleteResult.Success)
|
||||
return deleteResult;
|
||||
|
||||
var legacyDeleteResult = await this.DeleteSecretKey(secretId, LegacySecretKey(secretId));
|
||||
if (!legacyDeleteResult.Success)
|
||||
return legacyDeleteResult;
|
||||
|
||||
return deleteResult with { WasEntryFound = deleteResult.WasEntryFound || legacyDeleteResult.WasEntryFound };
|
||||
}
|
||||
|
||||
private async Task<RequestedSecret> GetLegacySecret(ISecretId secretId, bool isTrying)
|
||||
{
|
||||
var secretKey = LegacySecretKey(secretId);
|
||||
var secretRequest = new SelectSecretRequest(secretKey, Environment.UserName, isTrying);
|
||||
var result = await this.http.PostAsJsonAsync("/secrets/get", secretRequest, this.jsonRustSerializerOptions);
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
if(!isTrying)
|
||||
this.logger!.LogError($"Failed to get the secret data for '{secretKey}' due to an API issue: '{result.StatusCode}'");
|
||||
return new RequestedSecret(false, new EncryptedText(string.Empty), TB("Failed to get the secret data due to an API issue."));
|
||||
}
|
||||
|
||||
return await result.Content.ReadFromJsonAsync<RequestedSecret>(this.jsonRustSerializerOptions);
|
||||
}
|
||||
|
||||
private async Task<DeleteSecretResponse> DeleteSecretKey(ISecretId secretId, string secretKey)
|
||||
{
|
||||
var request = new SelectSecretRequest(secretKey, Environment.UserName, false);
|
||||
var result = await this.http.PostAsJsonAsync("/secrets/delete", request, this.jsonRustSerializerOptions);
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
this.logger!.LogError($"Failed to delete the secret data for secret ID '{secretId.SecretId}' due to an API issue: '{result.StatusCode}'");
|
||||
this.logger!.LogError($"Failed to delete the secret data for '{secretKey}' due to an API issue: '{result.StatusCode}'");
|
||||
return new DeleteSecretResponse{Success = false, WasEntryFound = false, Issue = TB("Failed to delete the secret data due to an API issue.")};
|
||||
}
|
||||
|
||||
var state = await result.Content.ReadFromJsonAsync<DeleteSecretResponse>(this.jsonRustSerializerOptions);
|
||||
if (!state.Success)
|
||||
this.logger!.LogError($"Failed to delete the secret data for secret ID '{secretId.SecretId}': '{state.Issue}'");
|
||||
this.logger!.LogError($"Failed to delete the secret data for '{secretKey}': '{state.Issue}'");
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user