diff --git a/app/MindWork AI Studio/Dialogs/DataSourceERIV1ExportDialog.razor b/app/MindWork AI Studio/Dialogs/DataSourceERIV1ExportDialog.razor
deleted file mode 100644
index 2d79acb6..00000000
--- a/app/MindWork AI Studio/Dialogs/DataSourceERIV1ExportDialog.razor
+++ /dev/null
@@ -1,52 +0,0 @@
-@using AIStudio.Tools.ERIClient.DataModel
-@inherits MSGComponentBase
-
-
-
-
- @string.Format(T("Export the ERI v1 data source '{0}' as Lua code for a configuration plugin."), this.DataSource.Name)
-
-
- @if (this.NeedsSecret())
- {
- @if (!this.HasConfiguredSecret)
- {
-
- @T("No secret is configured for this ERI data source. The export will contain a placeholder that you need to replace manually.")
-
- }
- else if (!this.CanEncryptSecret)
- {
-
- @T("No enterprise encryption secret is configured. The export will contain a placeholder that you need to replace manually.")
-
- }
-
-
- }
-
- @if (this.DataSource.AuthMethod is AuthMethod.USERNAME_PASSWORD)
- {
-
- @foreach (var mode in this.availableUsernamePasswordModes)
- {
-
- @this.GetUsernamePasswordModeText(mode)
-
- }
-
- }
-
-
-
- @T("Cancel")
-
-
- @T("Export")
-
-
-
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Dialogs/DataSourceERIV1ExportDialog.razor.cs b/app/MindWork AI Studio/Dialogs/DataSourceERIV1ExportDialog.razor.cs
deleted file mode 100644
index 905e8fc0..00000000
--- a/app/MindWork AI Studio/Dialogs/DataSourceERIV1ExportDialog.razor.cs
+++ /dev/null
@@ -1,83 +0,0 @@
-using AIStudio.Components;
-using AIStudio.Settings.DataModel;
-using AIStudio.Tools.ERIClient.DataModel;
-
-using Microsoft.AspNetCore.Components;
-
-namespace AIStudio.Dialogs;
-
-public partial class DataSourceERIV1ExportDialog : MSGComponentBase
-{
- [CascadingParameter]
- private IMudDialogInstance MudDialog { get; set; } = null!;
-
- [Parameter]
- public DataSourceERI_V1 DataSource { get; set; }
-
- [Parameter]
- public bool HasConfiguredSecret { get; set; }
-
- [Parameter]
- public bool CanEncryptSecret { get; set; }
-
- private readonly DataSourceERIUsernamePasswordMode[] availableUsernamePasswordModes =
- [
- DataSourceERIUsernamePasswordMode.OS_USERNAME_SHARED_PASSWORD,
- DataSourceERIUsernamePasswordMode.SHARED_USERNAME_AND_PASSWORD
- ];
-
- private bool includeSecret;
- private DataSourceERIUsernamePasswordMode usernamePasswordMode = DataSourceERIUsernamePasswordMode.OS_USERNAME_SHARED_PASSWORD;
-
- private bool CanIncludeSecret => this.HasConfiguredSecret && this.CanEncryptSecret;
-
- #region Overrides of ComponentBase
-
- protected override async Task OnInitializedAsync()
- {
- this.includeSecret = this.CanIncludeSecret;
- await base.OnInitializedAsync();
- }
-
- #endregion
-
- private bool NeedsSecret() => this.DataSource.AuthMethod is AuthMethod.TOKEN or AuthMethod.USERNAME_PASSWORD;
-
- private string GetIncludeSecretLabel() => this.DataSource.AuthMethod switch
- {
- AuthMethod.TOKEN => T("Include the configured access token in the export?"),
- AuthMethod.USERNAME_PASSWORD => T("Include the configured password in the export?"),
-
- _ => T("Include the configured secret in the export?"),
- };
-
- private string GetIncludeSecretLabelOn() => this.DataSource.AuthMethod switch
- {
- AuthMethod.TOKEN => T("Yes, export the encrypted access token"),
- AuthMethod.USERNAME_PASSWORD => T("Yes, export the encrypted password"),
-
- _ => T("Yes, export the encrypted secret"),
- };
-
- private string GetIncludeSecretLabelOff() => this.DataSource.AuthMethod switch
- {
- AuthMethod.TOKEN => T("No, use a token placeholder"),
- AuthMethod.USERNAME_PASSWORD => T("No, use a password placeholder"),
-
- _ => T("No, use a placeholder"),
- };
-
- private string GetUsernamePasswordModeText() => this.GetUsernamePasswordModeText(this.usernamePasswordMode);
-
- private string GetUsernamePasswordModeText(DataSourceERIUsernamePasswordMode mode) => mode switch
- {
- DataSourceERIUsernamePasswordMode.OS_USERNAME_SHARED_PASSWORD => T("Read each user's username from the operating system and share one password"),
- DataSourceERIUsernamePasswordMode.SHARED_USERNAME_AND_PASSWORD => T("Use the same username and password for all users"),
-
- _ => T("User-managed username and password"),
- };
-
- private void Cancel() => this.MudDialog.Cancel();
-
- private void Export() => this.MudDialog.Close(DialogResult.Ok(new DataSourceERIV1ExportDialogResult(this.includeSecret, this.usernamePasswordMode)));
-}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Dialogs/DataSourceERIV1ExportDialogResult.cs b/app/MindWork AI Studio/Dialogs/DataSourceERIV1ExportDialogResult.cs
deleted file mode 100644
index eca1eac9..00000000
--- a/app/MindWork AI Studio/Dialogs/DataSourceERIV1ExportDialogResult.cs
+++ /dev/null
@@ -1,5 +0,0 @@
-using AIStudio.Settings.DataModel;
-
-namespace AIStudio.Dialogs;
-
-public readonly record struct DataSourceERIV1ExportDialogResult(bool IncludeSecret, DataSourceERIUsernamePasswordMode UsernamePasswordMode);
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Dialogs/DataSourceERIV1UsernamePasswordExportDialog.razor b/app/MindWork AI Studio/Dialogs/DataSourceERIV1UsernamePasswordExportDialog.razor
new file mode 100644
index 00000000..088be8ea
--- /dev/null
+++ b/app/MindWork AI Studio/Dialogs/DataSourceERIV1UsernamePasswordExportDialog.razor
@@ -0,0 +1,26 @@
+@inherits MSGComponentBase
+
+
+
+
+ @string.Format(T("How should AI Studio export the username and password configuration for the ERI v1 data source '{0}'?"), this.DataSource.Name)
+
+
+
+ @foreach (var mode in this.availableUsernamePasswordModes)
+ {
+
+ @this.GetUsernamePasswordModeText(mode)
+
+ }
+
+
+
+
+ @T("Cancel")
+
+
+ @T("Export")
+
+
+
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Dialogs/DataSourceERIV1UsernamePasswordExportDialog.razor.cs b/app/MindWork AI Studio/Dialogs/DataSourceERIV1UsernamePasswordExportDialog.razor.cs
new file mode 100644
index 00000000..cf0ec960
--- /dev/null
+++ b/app/MindWork AI Studio/Dialogs/DataSourceERIV1UsernamePasswordExportDialog.razor.cs
@@ -0,0 +1,37 @@
+using AIStudio.Components;
+using AIStudio.Settings.DataModel;
+
+using Microsoft.AspNetCore.Components;
+
+namespace AIStudio.Dialogs;
+
+public partial class DataSourceERIV1UsernamePasswordExportDialog : MSGComponentBase
+{
+ [CascadingParameter]
+ private IMudDialogInstance MudDialog { get; set; } = null!;
+
+ [Parameter]
+ public DataSourceERI_V1 DataSource { get; set; }
+
+ private readonly DataSourceERIUsernamePasswordMode[] availableUsernamePasswordModes =
+ [
+ DataSourceERIUsernamePasswordMode.OS_USERNAME_SHARED_PASSWORD,
+ DataSourceERIUsernamePasswordMode.SHARED_USERNAME_AND_PASSWORD
+ ];
+
+ private DataSourceERIUsernamePasswordMode usernamePasswordMode = DataSourceERIUsernamePasswordMode.OS_USERNAME_SHARED_PASSWORD;
+
+ private string GetUsernamePasswordModeText() => this.GetUsernamePasswordModeText(this.usernamePasswordMode);
+
+ private string GetUsernamePasswordModeText(DataSourceERIUsernamePasswordMode mode) => mode switch
+ {
+ DataSourceERIUsernamePasswordMode.OS_USERNAME_SHARED_PASSWORD => T("Read each user's username from the operating system and share one password"),
+ DataSourceERIUsernamePasswordMode.SHARED_USERNAME_AND_PASSWORD => T("Use the same username and password for all users"),
+
+ _ => T("User-managed username and password"),
+ };
+
+ private void Cancel() => this.MudDialog.Cancel();
+
+ private void Export() => this.MudDialog.Close(DialogResult.Ok(new DataSourceERIV1UsernamePasswordExportDialogResult(this.usernamePasswordMode)));
+}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Dialogs/DataSourceERIV1UsernamePasswordExportDialogResult.cs b/app/MindWork AI Studio/Dialogs/DataSourceERIV1UsernamePasswordExportDialogResult.cs
new file mode 100644
index 00000000..907f920e
--- /dev/null
+++ b/app/MindWork AI Studio/Dialogs/DataSourceERIV1UsernamePasswordExportDialogResult.cs
@@ -0,0 +1,5 @@
+using AIStudio.Settings.DataModel;
+
+namespace AIStudio.Dialogs;
+
+public readonly record struct DataSourceERIV1UsernamePasswordExportDialogResult(DataSourceERIUsernamePasswordMode UsernamePasswordMode);
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogDataSources.razor.cs b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogDataSources.razor.cs
index f904f33f..ff706363 100644
--- a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogDataSources.razor.cs
+++ b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogDataSources.razor.cs
@@ -121,35 +121,66 @@ public partial class SettingsDialogDataSources : SettingsDialogBase
}
var secretResponse = await this.RustService.GetSecret(eriDataSource, SecretStoreType.DATA_SOURCE, isTrying: true);
- var canEncryptSecret = PluginFactory.EnterpriseEncryption?.IsAvailable == true;
-
- var dialogParameters = new DialogParameters
+ if (!secretResponse.Success)
{
- { x => x.DataSource, eriDataSource },
- { x => x.HasConfiguredSecret, secretResponse.Success },
- { x => x.CanEncryptSecret, canEncryptSecret },
- };
-
- var dialogReference = await this.DialogService.ShowAsync(T("Export ERI Data Source"), dialogParameters, DialogOptions.FULLSCREEN);
- var dialogResult = await dialogReference.Result;
- if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is not DataSourceERIV1ExportDialogResult exportResult)
+ await this.DialogService.ShowMessageBox(
+ T("Export ERI Data Source"),
+ string.Format(T("Cannot export this ERI data source because no authentication secret is configured. The issue was: {0}"), secretResponse.Issue),
+ T("Close"));
return;
+ }
- string? encryptedSecret = null;
- if (exportResult.IncludeSecret && secretResponse.Success)
+ var encryption = PluginFactory.EnterpriseEncryption;
+ if (encryption?.IsAvailable != true)
{
- var decryptedSecret = await secretResponse.Secret.Decrypt(Program.ENCRYPTION);
- var encryption = PluginFactory.EnterpriseEncryption;
- if (encryption?.TryEncrypt(decryptedSecret, out var encrypted) == true)
- encryptedSecret = encrypted;
- else
- this.Snackbar.Add(T("Cannot export the encrypted secret. A placeholder will be used instead."), Severity.Warning);
+ await this.DialogService.ShowMessageBox(
+ T("Export ERI Data Source"),
+ T("Cannot export this ERI data source because no enterprise encryption secret is configured."),
+ T("Close"));
+ return;
+ }
+
+ var usernamePasswordMode = DataSourceERIUsernamePasswordMode.USER_MANAGED;
+ if (eriDataSource.AuthMethod is AuthMethod.TOKEN)
+ {
+ var dialogParameters = new DialogParameters
+ {
+ { x => x.Message, T("This ERI data source has an access token configured. Do you want to include the encrypted access token in the export? Note: The recipient will need the same encryption secret to use the access token.") },
+ };
+
+ var dialogReference = await this.DialogService.ShowAsync(T("Export Access Token?"), dialogParameters, DialogOptions.FULLSCREEN);
+ var dialogResult = await dialogReference.Result;
+ if (dialogResult is null || dialogResult.Canceled)
+ return;
+ }
+ else if (eriDataSource.AuthMethod is AuthMethod.USERNAME_PASSWORD)
+ {
+ var dialogParameters = new DialogParameters
+ {
+ { x => x.DataSource, eriDataSource },
+ };
+
+ var dialogReference = await this.DialogService.ShowAsync(T("Export ERI Data Source"), dialogParameters, DialogOptions.FULLSCREEN);
+ var dialogResult = await dialogReference.Result;
+ if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is not DataSourceERIV1UsernamePasswordExportDialogResult exportResult)
+ return;
+
+ usernamePasswordMode = exportResult.UsernamePasswordMode;
+ }
+
+ var decryptedSecret = await secretResponse.Secret.Decrypt(Program.ENCRYPTION);
+ if (!encryption.TryEncrypt(decryptedSecret, out var encryptedSecret))
+ {
+ await this.DialogService.ShowMessageBox(
+ T("Export ERI Data Source"),
+ T("Cannot export this ERI data source because the authentication secret could not be encrypted."),
+ T("Close"));
+ return;
}
var luaCode = eriDataSource.ExportAsConfigurationSection(
encryptedSecret,
- exportResult.UsernamePasswordMode,
- needsSecret && string.IsNullOrWhiteSpace(encryptedSecret));
+ usernamePasswordMode);
if (string.IsNullOrWhiteSpace(luaCode))
return;
diff --git a/app/MindWork AI Studio/Settings/DataModel/DataSourceERI_V1.cs b/app/MindWork AI Studio/Settings/DataModel/DataSourceERI_V1.cs
index 59ecdc62..cd254751 100644
--- a/app/MindWork AI Studio/Settings/DataModel/DataSourceERI_V1.cs
+++ b/app/MindWork AI Studio/Settings/DataModel/DataSourceERI_V1.cs
@@ -277,9 +277,8 @@ public readonly record struct DataSourceERI_V1 : IERIDataSource
///
/// Optional encrypted token or password to include in the export.
/// The organization-managed username/password mode to export.
- /// Whether to include a placeholder for the encrypted secret.
/// A Lua configuration section string.
- public string ExportAsConfigurationSection(string? encryptedSecret = null, DataSourceERIUsernamePasswordMode usernamePasswordMode = DataSourceERIUsernamePasswordMode.USER_MANAGED, bool useSecretPlaceholder = false)
+ public string ExportAsConfigurationSection(string? encryptedSecret = null, DataSourceERIUsernamePasswordMode usernamePasswordMode = DataSourceERIUsernamePasswordMode.USER_MANAGED)
{
var secretLine = string.Empty;
var usernamePasswordModeLine = string.Empty;
@@ -288,7 +287,7 @@ public readonly record struct DataSourceERI_V1 : IERIDataSource
switch (this.AuthMethod)
{
case AuthMethod.TOKEN:
- secretLine = CreateSecretLine("Token", encryptedSecret, "ENC:v1:", useSecretPlaceholder);
+ secretLine = CreateSecretLine("Token", encryptedSecret);
break;
case AuthMethod.USERNAME_PASSWORD:
@@ -307,7 +306,7 @@ public readonly record struct DataSourceERI_V1 : IERIDataSource
""";
}
- secretLine = CreateSecretLine("Password", encryptedSecret, "ENC:v1:", useSecretPlaceholder);
+ secretLine = CreateSecretLine("Password", encryptedSecret);
break;
}
@@ -375,19 +374,13 @@ public readonly record struct DataSourceERI_V1 : IERIDataSource
return true;
}
- private static string CreateSecretLine(string fieldName, string? encryptedSecret, string placeholder, bool useSecretPlaceholder)
+ private static string CreateSecretLine(string fieldName, string? encryptedSecret)
{
- var secret = !string.IsNullOrWhiteSpace(encryptedSecret)
- ? encryptedSecret
- : useSecretPlaceholder
- ? placeholder
- : string.Empty;
-
- if (string.IsNullOrWhiteSpace(secret))
+ if (string.IsNullOrWhiteSpace(encryptedSecret))
return string.Empty;
return $"""
- ["{fieldName}"] = "{LuaTools.EscapeLuaString(secret)}",
+ ["{fieldName}"] = "{LuaTools.EscapeLuaString(encryptedSecret)}",
""";
}
diff --git a/app/MindWork AI Studio/Tools/Services/RustService.Secrets.cs b/app/MindWork AI Studio/Tools/Services/RustService.Secrets.cs
index de5a3db4..36ed6b6b 100644
--- a/app/MindWork AI Studio/Tools/Services/RustService.Secrets.cs
+++ b/app/MindWork AI Studio/Tools/Services/RustService.Secrets.cs
@@ -18,24 +18,16 @@ public sealed partial class RustService
public async Task GetSecret(ISecretId secretId, SecretStoreType storeType, bool isTrying = false)
{
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)
+ var secret = await this.GetSecretByKey(secretKey, isTrying || storeType is SecretStoreType.DATA_SOURCE);
+ if (secret.Success || storeType is not SecretStoreType.DATA_SOURCE)
+ return secret;
+
+ var legacySecretKey = LegacySecretKey(secretId);
+ var legacySecret = await this.GetSecretByKey(legacySecretKey, isTrying: true);
+ if (legacySecret.Success)
{
- 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."));
- }
-
- var secret = await result.Content.ReadFromJsonAsync(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;
- }
+ this.logger!.LogDebug($"Successfully retrieved the legacy data source secret for '{legacySecretKey}'.");
+ return legacySecret;
}
if (!secret.Success && !isTrying)
@@ -92,9 +84,8 @@ public sealed partial class RustService
return deleteResult with { WasEntryFound = deleteResult.WasEntryFound || legacyDeleteResult.WasEntryFound };
}
- private async Task GetLegacySecret(ISecretId secretId, bool isTrying)
+ private async Task GetSecretByKey(string secretKey, 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)