integrating the security rules and badge to the plugin overview page

This commit is contained in:
nilsk 2026-03-31 15:37:01 +02:00
parent 09d13e66c2
commit 3f2d76337a
2 changed files with 75 additions and 26 deletions

View File

@ -1,4 +1,5 @@
@using AIStudio.Tools.PluginSystem @using AIStudio.Tools.PluginSystem
@using AIStudio.Tools.PluginSystem.Assistants
@inherits MSGComponentBase @inherits MSGComponentBase
@attribute [Route(Routes.PLUGINS)] @attribute [Route(Routes.PLUGINS)]
@ -64,19 +65,25 @@
</MudTd> </MudTd>
<MudTd> <MudTd>
<MudStack Row="true" Spacing="1" AlignItems="AlignItems.Center"> <MudStack Row="true" Spacing="1" AlignItems="AlignItems.Center">
@if (context.Type is PluginType.ASSISTANT)
{
var assistantPlugin = PluginFactory.RunningPlugins.OfType<PluginAssistants>().FirstOrDefault(x => x.Id == context.Id);
<AssistantPluginSecurityCard Plugin="@assistantPlugin" Compact="@true" />
}
@if (context is { IsInternal: false, Type: not PluginType.CONFIGURATION }) @if (context is { IsInternal: false, Type: not PluginType.CONFIGURATION })
{ {
var isEnabled = this.SettingsManager.IsPluginEnabled(context); var isEnabled = this.SettingsManager.IsPluginEnabled(context);
<MudTooltip Text="@(isEnabled ? T("Disable plugin") : T("Enable plugin"))"> var activationSwitchDisabled = this.IsActivationSwitchDisabled(context, isEnabled);
<MudSwitch T="bool" Value="@isEnabled" ValueChanged="@(_ => this.PluginActivationStateChanged(context))"/> <MudTooltip Text="@this.GetActivationTooltip(context, isEnabled)">
<MudSwitch T="bool" Value="@isEnabled" ValueChanged="@(_ => this.PluginActivationStateChanged(context))" Disabled="@activationSwitchDisabled"/>
</MudTooltip> </MudTooltip>
} }
@if (context is { IsInternal: false } && !string.IsNullOrWhiteSpace(context.SourceURL)) @if (context is { IsInternal: false } && !string.IsNullOrWhiteSpace(context.SourceURL))
{ {
var sourceUrl = context.SourceURL; var sourceUrl = context.SourceURL;
var isSendingMail = IsSendingMail(sourceUrl); var isSendingMail = IsSendingMail(sourceUrl);
if(isSendingMail) if (isSendingMail)
{ {
<MudTooltip Text="@T("Send a mail")"> <MudTooltip Text="@T("Send a mail")">
<MudIconButton Icon="@Icons.Material.Filled.Email" Href="@sourceUrl" Target="_blank" Size="Size.Medium"/> <MudIconButton Icon="@Icons.Material.Filled.Email" Href="@sourceUrl" Target="_blank" Size="Size.Medium"/>

View File

@ -59,7 +59,7 @@ public partial class Plugins : MSGComponentBase
return; return;
} }
if (pluginMeta.Type is not PluginType.ASSISTANT || !this.AssistantPluginAuditSettings.RequireAuditBeforeActivation) if (pluginMeta.Type is not PluginType.ASSISTANT)
{ {
this.SettingsManager.ConfigurationData.EnabledPlugins.Add(pluginMeta.Id); this.SettingsManager.ConfigurationData.EnabledPlugins.Add(pluginMeta.Id);
await this.SettingsManager.StoreSettings(); await this.SettingsManager.StoreSettings();
@ -71,31 +71,37 @@ public partial class Plugins : MSGComponentBase
if (assistantPlugin is null) if (assistantPlugin is null)
return; return;
var pluginHash = assistantPlugin.ComputeAuditHash(); var securityState = PluginAssistantSecurityResolver.Resolve(this.SettingsManager, assistantPlugin);
var cachedAudit = this.SettingsManager.ConfigurationData.AssistantPluginAudits.FirstOrDefault(x => x.PluginId == pluginMeta.Id); if (securityState.RequiresAudit)
if (cachedAudit is not null && cachedAudit.PluginHash == pluginHash)
{ {
if (cachedAudit.Level < this.AssistantPluginAuditSettings.MinimumLevel && this.AssistantPluginAuditSettings.BlockActivationBelowMinimum) await this.OpenAssistantAuditDialogAsync(pluginMeta.Id);
{
await this.DialogService.ShowMessageBox(this.T("Assistant Audit"), $"{cachedAudit.Level.GetName()}: {cachedAudit.Summary}", this.T("Close"));
return;
}
if (cachedAudit.Level < this.AssistantPluginAuditSettings.MinimumLevel &&
!await this.ConfirmActivationBelowMinimumAsync(pluginMeta.Name, cachedAudit.Level))
{
return;
}
this.SettingsManager.ConfigurationData.EnabledPlugins.Add(pluginMeta.Id);
await this.SettingsManager.StoreSettings();
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
return; return;
} }
if (securityState.IsBelowMinimum && securityState.IsBlocked)
{
var blockedAudit = securityState.Audit;
if (blockedAudit is not null)
await this.DialogService.ShowMessageBox(this.T("Assistant Audit"), $"{blockedAudit.Level.GetName()}: {blockedAudit.Summary}", this.T("Close"));
return;
}
if (securityState.IsBelowMinimum && securityState.CanOverride &&
!await this.ConfirmActivationBelowMinimumAsync(pluginMeta.Name, securityState.Audit!.Level))
{
return;
}
this.SettingsManager.ConfigurationData.EnabledPlugins.Add(pluginMeta.Id);
await this.SettingsManager.StoreSettings();
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
}
private async Task OpenAssistantAuditDialogAsync(Guid pluginId)
{
var parameters = new DialogParameters<AssistantPluginAuditDialog> var parameters = new DialogParameters<AssistantPluginAuditDialog>
{ {
{ x => x.PluginId, pluginMeta.Id }, { x => x.PluginId, pluginId },
}; };
var dialog = await this.DialogService.ShowAsync<AssistantPluginAuditDialog>(this.T("Assistant Audit"), parameters, DialogOptions.FULLSCREEN); var dialog = await this.DialogService.ShowAsync<AssistantPluginAuditDialog>(this.T("Assistant Audit"), parameters, DialogOptions.FULLSCREEN);
var result = await dialog.Result; var result = await dialog.Result;
@ -106,7 +112,7 @@ public partial class Plugins : MSGComponentBase
this.UpsertAuditCard(auditResult.Audit); this.UpsertAuditCard(auditResult.Audit);
if (auditResult.ActivatePlugin) if (auditResult.ActivatePlugin)
this.SettingsManager.ConfigurationData.EnabledPlugins.Add(pluginMeta.Id); this.SettingsManager.ConfigurationData.EnabledPlugins.Add(pluginId);
await this.SettingsManager.StoreSettings(); await this.SettingsManager.StoreSettings();
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED); await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
@ -132,8 +138,44 @@ public partial class Plugins : MSGComponentBase
return dialogResult is not null && !dialogResult.Canceled; return dialogResult is not null && !dialogResult.Canceled;
} }
private bool IsActivationSwitchDisabled(IPluginMetadata pluginMeta, bool isEnabled)
{
if (isEnabled || pluginMeta.Type is not PluginType.ASSISTANT)
return false;
var assistantPlugin = this.TryGetAssistantPlugin(pluginMeta.Id);
if (assistantPlugin is null)
return false;
var securityState = PluginAssistantSecurityResolver.Resolve(this.SettingsManager, assistantPlugin);
return securityState.IsBlocked && !securityState.RequiresAudit;
}
private string GetActivationTooltip(IPluginMetadata pluginMeta, bool isEnabled)
{
if (isEnabled)
return this.T("Disable plugin");
if (pluginMeta.Type is not PluginType.ASSISTANT)
return this.T("Enable plugin");
var assistantPlugin = this.TryGetAssistantPlugin(pluginMeta.Id);
if (assistantPlugin is null)
return this.T("Enable plugin");
var securityState = PluginAssistantSecurityResolver.Resolve(this.SettingsManager, assistantPlugin);
if (securityState.RequiresAudit)
return securityState.ActionLabel;
return securityState.IsBlocked
? securityState.Description
: this.T("Enable plugin");
}
private static bool IsSendingMail(string sourceUrl) => sourceUrl.TrimStart().StartsWith("mailto:", StringComparison.OrdinalIgnoreCase); private static bool IsSendingMail(string sourceUrl) => sourceUrl.TrimStart().StartsWith("mailto:", StringComparison.OrdinalIgnoreCase);
private PluginAssistants? TryGetAssistantPlugin(Guid pluginId) => PluginFactory.RunningPlugins.OfType<PluginAssistants>().FirstOrDefault(x => x.Id == pluginId);
private void UpsertAuditCard(PluginAssistantAudit audit) private void UpsertAuditCard(PluginAssistantAudit audit)
{ {
var audits = this.SettingsManager.ConfigurationData.AssistantPluginAudits; var audits = this.SettingsManager.ConfigurationData.AssistantPluginAudits;
@ -150,7 +192,7 @@ public partial class Plugins : MSGComponentBase
{ {
switch (triggeredEvent) switch (triggeredEvent)
{ {
case Event.PLUGINS_RELOADED: case Event.PLUGINS_RELOADED or Event.CONFIGURATION_CHANGED:
await this.InvokeAsync(this.StateHasChanged); await this.InvokeAsync(this.StateHasChanged);
break; break;
} }