mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-03-29 13:51:37 +00:00
Improved detection of changed encryption secret
This commit is contained in:
parent
35305c168f
commit
af9b054491
@ -25,13 +25,22 @@ public static partial class PluginFactory
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the enterprise encryption service by reading the encryption secret
|
||||
/// from the Windows Registry or environment variables.
|
||||
/// from the effective enterprise source.
|
||||
/// </summary>
|
||||
/// <param name="rustService">The Rust service to use for reading the encryption secret.</param>
|
||||
public static async Task InitializeEnterpriseEncryption(Services.RustService rustService)
|
||||
{
|
||||
LOG.LogInformation("Initializing enterprise encryption service...");
|
||||
var encryptionSecret = await rustService.EnterpriseEnvConfigEncryptionSecret();
|
||||
InitializeEnterpriseEncryption(encryptionSecret);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the enterprise encryption service using a prefetched secret value.
|
||||
/// </summary>
|
||||
/// <param name="encryptionSecret">The base64-encoded enterprise encryption secret.</param>
|
||||
public static void InitializeEnterpriseEncryption(string? encryptionSecret)
|
||||
{
|
||||
LOG.LogInformation("Initializing enterprise encryption service...");
|
||||
var enterpriseEncryptionLogger = Program.LOGGER_FACTORY.CreateLogger<EnterpriseEncryption>();
|
||||
EnterpriseEncryption = new EnterpriseEncryption(enterpriseEncryptionLogger, encryptionSecret);
|
||||
|
||||
|
||||
@ -1,4 +1,8 @@
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
using AIStudio.Settings;
|
||||
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace AIStudio.Tools.Services;
|
||||
|
||||
@ -8,8 +12,14 @@ public sealed class EnterpriseEnvironmentService(ILogger<EnterpriseEnvironmentSe
|
||||
|
||||
public static bool HasValidEnterpriseSnapshot { get; private set; }
|
||||
|
||||
private static EnterpriseSecretSnapshot CURRENT_SECRET_SNAPSHOT;
|
||||
|
||||
private readonly record struct EnterpriseEnvironmentSnapshot(Guid ConfigurationId, string ConfigurationServerUrl, string? ETag);
|
||||
|
||||
private readonly record struct EnterpriseSecretSnapshot(bool HasSecret, string Fingerprint);
|
||||
|
||||
private readonly record struct EnterpriseSecretTarget(string SecretId, string SecretName, SecretStoreType StoreType) : ISecretId;
|
||||
|
||||
#if DEBUG
|
||||
private static readonly TimeSpan CHECK_INTERVAL = TimeSpan.FromMinutes(6);
|
||||
#else
|
||||
@ -39,6 +49,7 @@ public sealed class EnterpriseEnvironmentService(ILogger<EnterpriseEnvironmentSe
|
||||
logger.LogInformation("Start updating of the enterprise environment.");
|
||||
HasValidEnterpriseSnapshot = false;
|
||||
var previousSnapshot = BuildNormalizedSnapshot(CURRENT_ENVIRONMENTS);
|
||||
var previousSecretSnapshot = CURRENT_SECRET_SNAPSHOT;
|
||||
|
||||
//
|
||||
// Step 1: Fetch all active configurations.
|
||||
@ -55,6 +66,21 @@ public sealed class EnterpriseEnvironmentService(ILogger<EnterpriseEnvironmentSe
|
||||
return;
|
||||
}
|
||||
|
||||
string enterpriseEncryptionSecret;
|
||||
try
|
||||
{
|
||||
enterpriseEncryptionSecret = await rustService.EnterpriseEnvConfigEncryptionSecret();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogError(e, "Failed to fetch the enterprise encryption secret from the Rust service.");
|
||||
await MessageBus.INSTANCE.SendMessage(null, Event.RUST_SERVICE_UNAVAILABLE, "EnterpriseEnvConfigEncryptionSecret failed");
|
||||
return;
|
||||
}
|
||||
|
||||
var nextSecretSnapshot = await BuildSecretSnapshot(enterpriseEncryptionSecret);
|
||||
var wasSecretChanged = previousSecretSnapshot != nextSecretSnapshot;
|
||||
|
||||
//
|
||||
// Step 2: Determine ETags and build the list of reachable configurations.
|
||||
// IMPORTANT: when one config server fails, we continue with the others.
|
||||
@ -169,10 +195,20 @@ public sealed class EnterpriseEnvironmentService(ILogger<EnterpriseEnvironmentSe
|
||||
logger.LogInformation("AI Studio runs without any enterprise configurations.");
|
||||
|
||||
var effectiveSnapshot = BuildNormalizedSnapshot(effectiveEnvironments);
|
||||
|
||||
if (PluginFactory.IsInitialized && wasSecretChanged)
|
||||
{
|
||||
logger.LogInformation("The enterprise encryption secret changed. Refreshing the enterprise encryption service and reloading plugins.");
|
||||
PluginFactory.InitializeEnterpriseEncryption(enterpriseEncryptionSecret);
|
||||
await this.RemoveEnterpriseManagedApiKeysAsync();
|
||||
await PluginFactory.LoadAll();
|
||||
}
|
||||
|
||||
CURRENT_ENVIRONMENTS = effectiveEnvironments;
|
||||
CURRENT_SECRET_SNAPSHOT = nextSecretSnapshot;
|
||||
HasValidEnterpriseSnapshot = true;
|
||||
|
||||
if (!previousSnapshot.SequenceEqual(effectiveSnapshot))
|
||||
if (!previousSnapshot.SequenceEqual(effectiveSnapshot) || wasSecretChanged)
|
||||
await MessageBus.INSTANCE.SendMessage<bool>(null, Event.ENTERPRISE_ENVIRONMENTS_CHANGED);
|
||||
}
|
||||
catch (Exception e)
|
||||
@ -193,8 +229,81 @@ public sealed class EnterpriseEnvironmentService(ILogger<EnterpriseEnvironmentSe
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private static async Task<EnterpriseSecretSnapshot> BuildSecretSnapshot(string secret)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(secret))
|
||||
return new EnterpriseSecretSnapshot(false, string.Empty);
|
||||
|
||||
return new EnterpriseSecretSnapshot(true, await ComputeSecretFingerprint(secret));
|
||||
}
|
||||
|
||||
private static async Task<string> ComputeSecretFingerprint(string secret)
|
||||
{
|
||||
using var secretStream = new MemoryStream(Encoding.UTF8.GetBytes(secret));
|
||||
var hash = await SHA256.HashDataAsync(secretStream);
|
||||
return Convert.ToHexString(hash);
|
||||
}
|
||||
|
||||
private static string NormalizeServerUrl(string serverUrl)
|
||||
{
|
||||
return serverUrl.Trim().TrimEnd('/');
|
||||
}
|
||||
|
||||
private async Task RemoveEnterpriseManagedApiKeysAsync()
|
||||
{
|
||||
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.");
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogInformation("Removing {SecretCount} enterprise-managed API key(s) from the OS keyring after an enterprise encryption secret change.", secretTargets.Count);
|
||||
foreach (var target in secretTargets)
|
||||
{
|
||||
try
|
||||
{
|
||||
var deleteResult = 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);
|
||||
else
|
||||
logger.LogInformation("Enterprise-managed API key '{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);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogWarning(e, "Failed to delete enterprise-managed API key '{SecretName}' from the OS keyring.", target.SecretName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static List<EnterpriseSecretTarget> GetEnterpriseManagedSecretTargets()
|
||||
{
|
||||
var configurationData = Program.SERVICE_PROVIDER.GetRequiredService<SettingsManager>().ConfigurationData;
|
||||
var secretTargets = new HashSet<EnterpriseSecretTarget>();
|
||||
|
||||
AddEnterpriseManagedSecretTargets(configurationData.Providers, SecretStoreType.LLM_PROVIDER, secretTargets);
|
||||
AddEnterpriseManagedSecretTargets(configurationData.EmbeddingProviders, SecretStoreType.EMBEDDING_PROVIDER, secretTargets);
|
||||
AddEnterpriseManagedSecretTargets(configurationData.TranscriptionProviders, SecretStoreType.TRANSCRIPTION_PROVIDER, secretTargets);
|
||||
|
||||
return secretTargets.ToList();
|
||||
}
|
||||
|
||||
private static void AddEnterpriseManagedSecretTargets<TSecret>(
|
||||
IEnumerable<TSecret> secrets,
|
||||
SecretStoreType storeType,
|
||||
ISet<EnterpriseSecretTarget> secretTargets) where TSecret : ISecretId, IConfigurationObject
|
||||
{
|
||||
foreach (var secret in secrets)
|
||||
{
|
||||
if (!secret.IsEnterpriseConfiguration || secret.EnterpriseConfigurationPluginId == Guid.Empty)
|
||||
continue;
|
||||
|
||||
secretTargets.Add(new EnterpriseSecretTarget(secret.SecretId, secret.SecretName, storeType));
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user