Added an option to all data sources to select a local security policy

This commit is contained in:
Thorsten Sommer 2025-02-10 22:19:27 +01:00
parent ea671a1fcd
commit e85a75aa96
Signed by: tsommer
GPG Key ID: 371BBA77A02C0108
17 changed files with 151 additions and 2 deletions

View File

@ -1,4 +1,6 @@
@using AIStudio.Settings.DataModel
@using AIStudio.Tools.ERIClient.DataModel @using AIStudio.Tools.ERIClient.DataModel
<MudDialog> <MudDialog>
<DialogContent> <DialogContent>
@ -25,7 +27,8 @@
} }
<TextInfoLines Label="Server description" MaxLines="14" Value="@this.serverDescription" ClipboardTooltipSubject="the server description"/> <TextInfoLines Label="Server description" MaxLines="14" Value="@this.serverDescription" ClipboardTooltipSubject="the server description"/>
<TextInfoLines Label="Security requirements" MaxLines="3" Value="@this.securityRequirements.Explain()" ClipboardTooltipSubject="the security requirements"/> <TextInfoLines Label="Security requirements of the data provider" MaxLines="3" Value="@this.securityRequirements.Explain()" ClipboardTooltipSubject="the security requirements of the data provider"/>
<TextInfoLines Label="Your security policy" MaxLines="3" Value="@this.DataSource.SecurityPolicy.ToInfoText()" Color="@this.DataSource.SecurityPolicy.GetColor()" ClipboardTooltipSubject="your security policy"/>
<MudText Typo="Typo.h5" Class="mt-6"> <MudText Typo="Typo.h5" Class="mt-6">
Retrieval information Retrieval information

View File

@ -1,3 +1,4 @@
@using AIStudio.Settings.DataModel
@using AIStudio.Tools.ERIClient.DataModel @using AIStudio.Tools.ERIClient.DataModel
<MudDialog> <MudDialog>
<DialogContent> <DialogContent>
@ -109,6 +110,13 @@
UserAttributes="@SPELLCHECK_ATTRIBUTES"/> UserAttributes="@SPELLCHECK_ATTRIBUTES"/>
} }
<MudSelect @bind-Value="@this.dataSecurityPolicy" Text="@this.dataSecurityPolicy.ToSelectionText()" Label="Your security policy" Class="mb-3" OpenIcon="@Icons.Material.Filled.ExpandMore" AdornmentColor="Color.Info" Adornment="Adornment.Start" Validation="@this.dataSourceValidation.ValidateSecurityPolicy">
@foreach (var policy in Enum.GetValues<DataSourceSecurity>())
{
<MudSelectItem Value="@policy">@policy.ToSelectionText()</MudSelectItem>
}
</MudSelect>
</MudForm> </MudForm>
<Issues IssuesData="@this.dataIssues"/> <Issues IssuesData="@this.dataIssues"/>
</DialogContent> </DialogContent>

View File

@ -45,6 +45,8 @@ public partial class DataSourceERI_V1Dialog : ComponentBase, ISecretId
private string dataSecretStorageIssue = string.Empty; private string dataSecretStorageIssue = string.Empty;
private string dataEditingPreviousInstanceName = string.Empty; private string dataEditingPreviousInstanceName = string.Empty;
private List<AuthMethod> availableAuthMethods = []; private List<AuthMethod> availableAuthMethods = [];
private DataSourceSecurity dataSecurityPolicy;
private SecurityRequirements dataSourceSecurityRequirements;
private bool connectionTested; private bool connectionTested;
private bool connectionSuccessfulTested; private bool connectionSuccessfulTested;
@ -71,6 +73,7 @@ public partial class DataSourceERI_V1Dialog : ComponentBase, ISecretId
GetTestedConnection = () => this.connectionTested, GetTestedConnection = () => this.connectionTested,
GetTestedConnectionResult = () => this.connectionSuccessfulTested, GetTestedConnectionResult = () => this.connectionSuccessfulTested,
GetAvailableAuthMethods = () => this.availableAuthMethods, GetAvailableAuthMethods = () => this.availableAuthMethods,
GetSecurityRequirements = () => this.dataSourceSecurityRequirements,
}; };
} }
@ -147,6 +150,7 @@ public partial class DataSourceERI_V1Dialog : ComponentBase, ISecretId
AuthMethod = this.dataAuthMethod, AuthMethod = this.dataAuthMethod,
Username = this.dataUsername, Username = this.dataUsername,
Type = DataSourceType.ERI_V1, Type = DataSourceType.ERI_V1,
SecurityPolicy = this.dataSecurityPolicy,
}; };
} }
@ -196,6 +200,28 @@ public partial class DataSourceERI_V1Dialog : ComponentBase, ISecretId
this.availableAuthMethods = authSchemes.Data!.Select(n => n.AuthMethod).ToList(); this.availableAuthMethods = authSchemes.Data!.Select(n => n.AuthMethod).ToList();
var loginResult = await client.AuthenticateAsync(this.DataSource, this.RustService, cts.Token);
if (!loginResult.Successful)
{
await this.form.Validate();
Array.Resize(ref this.dataIssues, this.dataIssues.Length + 1);
this.dataIssues[^1] = loginResult.Message;
return;
}
var securityRequirementsRequest = await client.GetSecurityRequirementsAsync(cts.Token);
if (!securityRequirementsRequest.Successful)
{
await this.form.Validate();
Array.Resize(ref this.dataIssues, this.dataIssues.Length + 1);
this.dataIssues[^1] = securityRequirementsRequest.Message;
return;
}
this.dataSourceSecurityRequirements = securityRequirementsRequest.Data;
this.connectionTested = true; this.connectionTested = true;
this.connectionSuccessfulTested = true; this.connectionSuccessfulTested = true;
this.Logger.LogInformation("Connection to the ERI v1 server was successful tested."); this.Logger.LogInformation("Connection to the ERI v1 server was successful tested.");

View File

@ -1,3 +1,5 @@
@using AIStudio.Settings.DataModel
<MudDialog> <MudDialog>
<DialogContent> <DialogContent>
<MudForm @ref="@this.form" @bind-IsValid="@this.dataIsValid" @bind-Errors="@this.dataIssues"> <MudForm @ref="@this.form" @bind-IsValid="@this.dataIsValid" @bind-Errors="@this.dataIssues">
@ -60,6 +62,13 @@
</MudJustifiedText> </MudJustifiedText>
} }
} }
<MudSelect @bind-Value="@this.dataSecurityPolicy" Text="@this.dataSecurityPolicy.ToSelectionText()" Label="Your security policy" Class="mb-3" OpenIcon="@Icons.Material.Filled.ExpandMore" AdornmentColor="Color.Info" Adornment="Adornment.Start" Validation="@this.dataSourceValidation.ValidateSecurityPolicy">
@foreach (var policy in Enum.GetValues<DataSourceSecurity>())
{
<MudSelectItem Value="@policy">@policy.ToSelectionText()</MudSelectItem>
}
</MudSelect>
</MudForm> </MudForm>
<Issues IssuesData="@this.dataIssues"/> <Issues IssuesData="@this.dataIssues"/>
</DialogContent> </DialogContent>

View File

@ -42,6 +42,7 @@ public partial class DataSourceLocalDirectoryDialog : ComponentBase
private bool dataUserAcknowledgedCloudEmbedding; private bool dataUserAcknowledgedCloudEmbedding;
private string dataEmbeddingId = string.Empty; private string dataEmbeddingId = string.Empty;
private string dataPath = string.Empty; private string dataPath = string.Empty;
private DataSourceSecurity dataSecurityPolicy;
// We get the form reference from Blazor code to validate it manually: // We get the form reference from Blazor code to validate it manually:
private MudForm form = null!; private MudForm form = null!;
@ -102,6 +103,7 @@ public partial class DataSourceLocalDirectoryDialog : ComponentBase
Type = DataSourceType.LOCAL_DIRECTORY, Type = DataSourceType.LOCAL_DIRECTORY,
EmbeddingId = this.dataEmbeddingId, EmbeddingId = this.dataEmbeddingId,
Path = this.dataPath, Path = this.dataPath,
SecurityPolicy = this.dataSecurityPolicy,
}; };
private async Task Store() private async Task Store()

View File

@ -1,3 +1,5 @@
@using AIStudio.Settings.DataModel
<MudDialog> <MudDialog>
<DialogContent> <DialogContent>
<TextInfoLine Icon="@Icons.Material.Filled.Tag" Label="Data source name" Value="@this.DataSource.Name" ClipboardTooltipSubject="the data source name"/> <TextInfoLine Icon="@Icons.Material.Filled.Tag" Label="Data source name" Value="@this.DataSource.Name" ClipboardTooltipSubject="the data source name"/>
@ -31,6 +33,8 @@
</MudJustifiedText> </MudJustifiedText>
} }
<TextInfoLines Label="Your security policy" MaxLines="3" Value="@this.DataSource.SecurityPolicy.ToInfoText()" Color="@this.DataSource.SecurityPolicy.GetColor()" ClipboardTooltipSubject="your security policy"/>
<TextInfoLine Icon="@Icons.Material.Filled.SquareFoot" Label="Number of files" Value="@this.NumberFilesInDirectory" ClipboardTooltipSubject="the number of files in the directory"/> <TextInfoLine Icon="@Icons.Material.Filled.SquareFoot" Label="Number of files" Value="@this.NumberFilesInDirectory" ClipboardTooltipSubject="the number of files in the directory"/>
<TextInfoLines Label="Files list" MaxLines="14" Value="@this.directoryFiles.ToString()" ClipboardTooltipSubject="the files list"/> <TextInfoLines Label="Files list" MaxLines="14" Value="@this.directoryFiles.ToString()" ClipboardTooltipSubject="the files list"/>
@if (this.directorySizeNumFiles > 100) @if (this.directorySizeNumFiles > 100)

View File

@ -1,3 +1,4 @@
@using AIStudio.Settings.DataModel
<MudDialog> <MudDialog>
<DialogContent> <DialogContent>
<MudForm @ref="@this.form" @bind-IsValid="@this.dataIsValid" @bind-Errors="@this.dataIssues"> <MudForm @ref="@this.form" @bind-IsValid="@this.dataIsValid" @bind-Errors="@this.dataIssues">
@ -59,6 +60,13 @@
</MudJustifiedText> </MudJustifiedText>
} }
} }
<MudSelect @bind-Value="@this.dataSecurityPolicy" Text="@this.dataSecurityPolicy.ToSelectionText()" Label="Your security policy" Class="mb-3" OpenIcon="@Icons.Material.Filled.ExpandMore" AdornmentColor="Color.Info" Adornment="Adornment.Start" Validation="@this.dataSourceValidation.ValidateSecurityPolicy">
@foreach (var policy in Enum.GetValues<DataSourceSecurity>())
{
<MudSelectItem Value="@policy">@policy.ToSelectionText()</MudSelectItem>
}
</MudSelect>
</MudForm> </MudForm>
<Issues IssuesData="@this.dataIssues"/> <Issues IssuesData="@this.dataIssues"/>
</DialogContent> </DialogContent>

View File

@ -42,6 +42,7 @@ public partial class DataSourceLocalFileDialog : ComponentBase
private bool dataUserAcknowledgedCloudEmbedding; private bool dataUserAcknowledgedCloudEmbedding;
private string dataEmbeddingId = string.Empty; private string dataEmbeddingId = string.Empty;
private string dataFilePath = string.Empty; private string dataFilePath = string.Empty;
private DataSourceSecurity dataSecurityPolicy;
// We get the form reference from Blazor code to validate it manually: // We get the form reference from Blazor code to validate it manually:
private MudForm form = null!; private MudForm form = null!;
@ -102,6 +103,7 @@ public partial class DataSourceLocalFileDialog : ComponentBase
Type = DataSourceType.LOCAL_FILE, Type = DataSourceType.LOCAL_FILE,
EmbeddingId = this.dataEmbeddingId, EmbeddingId = this.dataEmbeddingId,
FilePath = this.dataFilePath, FilePath = this.dataFilePath,
SecurityPolicy = this.dataSecurityPolicy,
}; };
private async Task Store() private async Task Store()

View File

@ -1,3 +1,5 @@
@using AIStudio.Settings.DataModel
<MudDialog> <MudDialog>
<DialogContent> <DialogContent>
<TextInfoLine Icon="@Icons.Material.Filled.Tag" Label="Data source name" Value="@this.DataSource.Name" ClipboardTooltipSubject="the data source name"/> <TextInfoLine Icon="@Icons.Material.Filled.Tag" Label="Data source name" Value="@this.DataSource.Name" ClipboardTooltipSubject="the data source name"/>
@ -31,6 +33,7 @@
</MudJustifiedText> </MudJustifiedText>
} }
<TextInfoLines Label="Your security policy" MaxLines="3" Value="@this.DataSource.SecurityPolicy.ToInfoText()" Color="@this.DataSource.SecurityPolicy.GetColor()" ClipboardTooltipSubject="your security policy"/>
<TextInfoLine Icon="@Icons.Material.Filled.SquareFoot" Label="File size" Value="@this.FileSize" ClipboardTooltipSubject="the file size"/> <TextInfoLine Icon="@Icons.Material.Filled.SquareFoot" Label="File size" Value="@this.FileSize" ClipboardTooltipSubject="the file size"/>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>

View File

@ -36,4 +36,7 @@ public readonly record struct DataSourceERI_V1 : IERIDataSource
/// <inheritdoc /> /// <inheritdoc />
public string Username { get; init; } = string.Empty; public string Username { get; init; } = string.Empty;
/// <inheritdoc />
public DataSourceSecurity SecurityPolicy { get; init; } = DataSourceSecurity.NOT_SPECIFIED;
} }

View File

@ -24,6 +24,9 @@ public readonly record struct DataSourceLocalDirectory : IInternalDataSource
/// <inheritdoc /> /// <inheritdoc />
public string EmbeddingId { get; init; } = Guid.Empty.ToString(); public string EmbeddingId { get; init; } = Guid.Empty.ToString();
/// <inheritdoc />
public DataSourceSecurity SecurityPolicy { get; init; } = DataSourceSecurity.NOT_SPECIFIED;
/// <summary> /// <summary>
/// The path to the directory. /// The path to the directory.
/// </summary> /// </summary>

View File

@ -24,6 +24,9 @@ public readonly record struct DataSourceLocalFile : IInternalDataSource
/// <inheritdoc /> /// <inheritdoc />
public string EmbeddingId { get; init; } = Guid.Empty.ToString(); public string EmbeddingId { get; init; } = Guid.Empty.ToString();
/// <inheritdoc />
public DataSourceSecurity SecurityPolicy { get; init; } = DataSourceSecurity.NOT_SPECIFIED;
/// <summary> /// <summary>
/// The path to the file. /// The path to the file.
/// </summary> /// </summary>

View File

@ -0,0 +1,19 @@
namespace AIStudio.Settings.DataModel;
public enum DataSourceSecurity
{
/// <summary>
/// The security of the data source is not specified yet.
/// </summary>
NOT_SPECIFIED,
/// <summary>
/// This data can be used with any LLM provider.
/// </summary>
ALLOW_ANY,
/// <summary>
/// This data can only be used for self-hosted LLM providers.
/// </summary>
SELF_HOSTED,
}

View File

@ -0,0 +1,32 @@
namespace AIStudio.Settings.DataModel;
public static class DataSourceSecurityExtensions
{
public static string ToSelectionText(this DataSourceSecurity security) => security switch
{
DataSourceSecurity.NOT_SPECIFIED => "Please select a security policy",
DataSourceSecurity.ALLOW_ANY => "This data source can be used with any LLM provider. Your data may be sent to a cloud-based provider.",
DataSourceSecurity.SELF_HOSTED => "This data source can only be used with a self-hosted LLM provider. Your data will not be sent to any cloud-based provider.",
_ => "Unknown security policy"
};
public static string ToInfoText(this DataSourceSecurity security) => security switch
{
DataSourceSecurity.NOT_SPECIFIED => "The security of the data source is not specified yet. You cannot use this data source until you specify a security policy.",
DataSourceSecurity.ALLOW_ANY => "This data source can be used with any LLM provider. Your data may be sent to a cloud-based provider.",
DataSourceSecurity.SELF_HOSTED => "This data source can only be used with a self-hosted LLM provider. Your data will not be sent to any cloud-based provider.",
_ => "Unknown security policy"
};
public static TextColor GetColor(this DataSourceSecurity security) => security switch
{
DataSourceSecurity.ALLOW_ANY => TextColor.WARN,
DataSourceSecurity.SELF_HOSTED => TextColor.SUCCESS,
_ => TextColor.ERROR
};
}

View File

@ -32,4 +32,9 @@ public interface IDataSource
/// Which type of data source is this? /// Which type of data source is this?
/// </summary> /// </summary>
public DataSourceType Type { get; init; } public DataSourceType Type { get; init; }
/// <summary>
/// Which data security policy is applied to this data source?
/// </summary>
public DataSourceSecurity SecurityPolicy { get; init; }
} }

View File

@ -1,3 +1,4 @@
using AIStudio.Settings.DataModel;
using AIStudio.Tools.ERIClient.DataModel; using AIStudio.Tools.ERIClient.DataModel;
namespace AIStudio.Tools.Validation; namespace AIStudio.Tools.Validation;
@ -12,6 +13,8 @@ public sealed class DataSourceValidation
public Func<AuthMethod> GetAuthMethod { get; init; } = () => AuthMethod.NONE; public Func<AuthMethod> GetAuthMethod { get; init; } = () => AuthMethod.NONE;
public Func<SecurityRequirements?> GetSecurityRequirements { get; init; } = () => null;
public Func<bool> GetSelectedCloudEmbedding { get; init; } = () => false; public Func<bool> GetSelectedCloudEmbedding { get; init; } = () => false;
public Func<bool> GetTestedConnection { get; init; } = () => false; public Func<bool> GetTestedConnection { get; init; } = () => false;
@ -42,6 +45,21 @@ public sealed class DataSourceValidation
return null; return null;
} }
public string? ValidateSecurityPolicy(DataSourceSecurity securityPolicy)
{
if(securityPolicy is DataSourceSecurity.NOT_SPECIFIED)
return "Please select your security policy.";
var dataSourceSecurity = this.GetSecurityRequirements();
if (dataSourceSecurity is null)
return null;
if(dataSourceSecurity.Value.AllowedProviderType is ProviderType.SELF_HOSTED && securityPolicy is not DataSourceSecurity.SELF_HOSTED)
return "This data source can only be used with a self-hosted LLM provider. Please change the security policy.";
return null;
}
public string? ValidateUsername(string username) public string? ValidateUsername(string username)
{ {
if(this.GetAuthMethod() is not AuthMethod.USERNAME_PASSWORD) if(this.GetAuthMethod() is not AuthMethod.USERNAME_PASSWORD)

View File

@ -1,2 +1,3 @@
# v0.9.29, build 204 (2025-02-xx xx:xx UTC) # v0.9.29, build 204 (2025-02-xx xx:xx UTC)
- Added the possibility to select data sources for chats. This preview feature is hidden behind the RAG feature flag, check your app options in case you want to enable it. - Added the possibility to select data sources for chats. This preview feature is hidden behind the RAG feature flag, check your app options in case you want to enable it.
- Added an option to all data sources to select a local security policy. This preview feature is hidden behind the RAG feature flag.