mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-05-21 19:12:16 +00:00
security card component that shows all information of the current saved audit like activation state, policy and meta data
This commit is contained in:
parent
75407a8e10
commit
5f94428204
@ -0,0 +1,191 @@
|
|||||||
|
@using AIStudio.Agents.AssistantAudit
|
||||||
|
@inherits MSGComponentBase
|
||||||
|
|
||||||
|
@if (this.Plugin is not null)
|
||||||
|
{
|
||||||
|
var state = this.SecurityState;
|
||||||
|
|
||||||
|
<div class="d-flex">
|
||||||
|
<MudTooltip Text="@state.ActionLabel" Placement="Placement.Top">
|
||||||
|
<MudIconButton Icon="@state.BadgeIcon"
|
||||||
|
Color="@state.AuditColor"
|
||||||
|
Size="@(this.Compact ? Size.Small : Size.Medium)"
|
||||||
|
OnClick="@this.ToggleSecurityCard" />
|
||||||
|
</MudTooltip>
|
||||||
|
|
||||||
|
<MudPopover Open="@this.showSecurityCard"
|
||||||
|
AnchorOrigin="Origin.BottomRight"
|
||||||
|
TransformOrigin="Origin.BottomLeft"
|
||||||
|
OverflowBehavior="OverflowBehavior.FlipAlways"
|
||||||
|
DropShadow="@true"
|
||||||
|
Class="border-solid border-4 rounded-lg"
|
||||||
|
Style="@this.GetPopoverStyle()">
|
||||||
|
<MudCard Elevation="2" Outlined Style="max-width: min(42rem, 90vw);">
|
||||||
|
<MudCardHeader>
|
||||||
|
<CardHeaderAvatar>
|
||||||
|
<MudAvatar Color="@state.AuditColor" Variant="Variant.Filled" Size="Size.Large">
|
||||||
|
<MudIcon Icon="@state.AuditIcon" Size="Size.Medium" />
|
||||||
|
</MudAvatar>
|
||||||
|
</CardHeaderAvatar>
|
||||||
|
<CardHeaderContent>
|
||||||
|
<div class="d-flex align-center gap-2">
|
||||||
|
<MudText Typo="Typo.h6">@T("Assistant Security")</MudText>
|
||||||
|
<MudChip T="string" Size="Size.Small" Variant="Variant.Filled" Color="@state.AuditColor">
|
||||||
|
@state.AuditLabel
|
||||||
|
</MudChip>
|
||||||
|
@if (!string.IsNullOrWhiteSpace(state.AvailabilityLabel))
|
||||||
|
{
|
||||||
|
<MudChip T="string" Size="Size.Small" Variant="Variant.Outlined" Color="@state.AvailabilityColor" Icon="@state.AvailabilityIcon">
|
||||||
|
@state.AvailabilityLabel
|
||||||
|
</MudChip>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<MudText Typo="Typo.body2" Class="mud-text-secondary">
|
||||||
|
@state.Headline
|
||||||
|
</MudText>
|
||||||
|
</CardHeaderContent>
|
||||||
|
<CardHeaderActions>
|
||||||
|
<MudTooltip Text="@T("Show or hide the detailed security information.")">
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.ExpandMore" OnClick="@this.ToggleDetails" />
|
||||||
|
</MudTooltip>
|
||||||
|
</CardHeaderActions>
|
||||||
|
</MudCardHeader>
|
||||||
|
|
||||||
|
<MudCardContent Class="pt-0 pb-2">
|
||||||
|
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="4" Class="flex-wrap">
|
||||||
|
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="1">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.Speed" Size="Size.Small" />
|
||||||
|
<MudText Typo="Typo.body2">@T("Confidence"):</MudText>
|
||||||
|
<MudProgressLinear Color="@state.AuditColor"
|
||||||
|
Value="@this.GetConfidencePercentage()"
|
||||||
|
Rounded="@true"
|
||||||
|
Size="Size.Medium"
|
||||||
|
Style="width: 80px; min-width: 80px;" />
|
||||||
|
<MudText Typo="Typo.caption" Class="mud-text-secondary">
|
||||||
|
@this.GetConfidenceLabel()
|
||||||
|
</MudText>
|
||||||
|
</MudStack>
|
||||||
|
<MudDivider Vertical="@true" FlexItem="@true" />
|
||||||
|
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="1">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.BugReport" Size="Size.Small" Color="@state.AuditColor" />
|
||||||
|
<MudText Typo="Typo.body2">@this.GetFindingSummary()</MudText>
|
||||||
|
</MudStack>
|
||||||
|
<MudDivider Vertical="@true" FlexItem="@true" />
|
||||||
|
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="1">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.Schedule" Size="Size.Small" />
|
||||||
|
<MudText Typo="Typo.body2" Class="mud-text-secondary">
|
||||||
|
@this.GetAuditTimestampLabel()
|
||||||
|
</MudText>
|
||||||
|
</MudStack>
|
||||||
|
</MudStack>
|
||||||
|
</MudCardContent>
|
||||||
|
|
||||||
|
<MudCollapse Expanded="@this.showDetails">
|
||||||
|
<MudDivider />
|
||||||
|
<MudCardContent>
|
||||||
|
<MudStack Spacing="3">
|
||||||
|
<MudAlert Severity="@this.GetStatusSeverity()" Variant="Variant.Outlined" Dense="@true">
|
||||||
|
@state.Description
|
||||||
|
</MudAlert>
|
||||||
|
|
||||||
|
<MudSimpleTable Dense="@true" Hover="@true" Bordered="@true" Striped="@true" Style="overflow-x: auto;">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="width: 180px;">
|
||||||
|
<MudText Typo="Typo.body2"><b>@T("Plugin ID")</b></MudText>
|
||||||
|
</td>
|
||||||
|
<td><code style="font-size: 0.8rem;">@this.Plugin.Id</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<MudText Typo="Typo.body2"><b>@T("Current hash")</b></MudText>
|
||||||
|
</td>
|
||||||
|
<td><code style="font-size: 0.8rem;">@GetShortHash(state.CurrentHash)</code></td>
|
||||||
|
</tr>
|
||||||
|
@if (state.Audit is not null)
|
||||||
|
{
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<MudText Typo="Typo.body2"><b>@T("Audit hash")</b></MudText>
|
||||||
|
</td>
|
||||||
|
<td><code style="font-size: 0.8rem;">@GetShortHash(state.Audit.PluginHash)</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<MudText Typo="Typo.body2"><b>@T("Audit provider")</b></MudText>
|
||||||
|
</td>
|
||||||
|
<td><MudText Typo="Typo.body2">@this.GetAuditProviderLabel()</MudText></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<MudText Typo="Typo.body2"><b>@T("Audited at")</b></MudText>
|
||||||
|
</td>
|
||||||
|
<td><MudText Typo="Typo.body2">@this.FormatFileTimestamp(state.Audit.AuditedAtUtc.ToLocalTime().DateTime)</MudText></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<MudText Typo="Typo.body2"><b>@T("Audit level")</b></MudText>
|
||||||
|
</td>
|
||||||
|
<td><MudText Typo="Typo.body2">@state.AuditLabel</MudText></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<MudText Typo="Typo.body2"><b>@T("Availability")</b></MudText>
|
||||||
|
</td>
|
||||||
|
<td><MudText Typo="Typo.body2">@state.AvailabilityLabel</MudText></td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<MudText Typo="Typo.body2"><b>@T("Required minimum")</b></MudText>
|
||||||
|
</td>
|
||||||
|
<td><MudText Typo="Typo.body2">@state.Settings.MinimumLevel.GetName()</MudText></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</MudSimpleTable>
|
||||||
|
|
||||||
|
@if (state.Audit is null)
|
||||||
|
{
|
||||||
|
<MudAlert Severity="Severity.Info" Variant="Variant.Text" Dense="@true">
|
||||||
|
@T("No stored audit details are available yet.")
|
||||||
|
</MudAlert>
|
||||||
|
}
|
||||||
|
else if (state.Audit.Findings.Count == 0)
|
||||||
|
{
|
||||||
|
<MudAlert Severity="Severity.Success" Variant="Variant.Text" Dense="@true">
|
||||||
|
@T("No security findings were stored for this assistant plugin.")
|
||||||
|
</MudAlert>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudStack Spacing="2">
|
||||||
|
@foreach (var finding in state.Audit.Findings)
|
||||||
|
{
|
||||||
|
<MudAlert Severity="@finding.Severity.GetSeverity()" Variant="Variant.Text" Dense="@true">
|
||||||
|
<strong>@finding.Category</strong><span>: @finding.Description</span>
|
||||||
|
@if (!string.IsNullOrWhiteSpace(finding.Location))
|
||||||
|
{
|
||||||
|
<div>
|
||||||
|
<MudText Typo="Typo.caption">@finding.Location</MudText>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</MudAlert>
|
||||||
|
}
|
||||||
|
</MudStack>
|
||||||
|
}
|
||||||
|
</MudStack>
|
||||||
|
</MudCardContent>
|
||||||
|
</MudCollapse>
|
||||||
|
|
||||||
|
<MudCardActions>
|
||||||
|
<MudButton Variant="Variant.Outlined" Color="Color.Primary" OnClick="@this.OpenAuditDialogAsync">
|
||||||
|
@state.ActionLabel
|
||||||
|
</MudButton>
|
||||||
|
<MudButton Variant="Variant.Text" OnClick="@this.HideSecurityCard">
|
||||||
|
@T("Close")
|
||||||
|
</MudButton>
|
||||||
|
</MudCardActions>
|
||||||
|
</MudCard>
|
||||||
|
</MudPopover>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
@ -0,0 +1,152 @@
|
|||||||
|
using System.Globalization;
|
||||||
|
using AIStudio.Dialogs;
|
||||||
|
using AIStudio.Tools.PluginSystem.Assistants;
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using DialogOptions = AIStudio.Dialogs.DialogOptions;
|
||||||
|
|
||||||
|
namespace AIStudio.Components;
|
||||||
|
|
||||||
|
public partial class AssistantPluginSecurityCard : MSGComponentBase
|
||||||
|
{
|
||||||
|
[Parameter]
|
||||||
|
public PluginAssistants? Plugin { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public bool Compact { get; set; }
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private IDialogService DialogService { get; init; } = null!;
|
||||||
|
|
||||||
|
private PluginAssistantSecurityState SecurityState => this.Plugin is null
|
||||||
|
? new PluginAssistantSecurityState()
|
||||||
|
: PluginAssistantSecurityResolver.Resolve(this.SettingsManager, this.Plugin);
|
||||||
|
|
||||||
|
private CultureInfo currentCultureInfo = CultureInfo.InvariantCulture;
|
||||||
|
private bool showSecurityCard;
|
||||||
|
private bool showDetails;
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
var activeLanguagePlugin = await this.SettingsManager.GetActiveLanguagePlugin();
|
||||||
|
this.currentCultureInfo = CommonTools.DeriveActiveCultureOrInvariant(activeLanguagePlugin.IETFTag);
|
||||||
|
this.showDetails = !this.Compact;
|
||||||
|
|
||||||
|
this.ApplyFilters([], [ Event.CONFIGURATION_CHANGED, Event.PLUGINS_RELOADED ]);
|
||||||
|
await base.OnInitializedAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Task OnAfterRenderAsync(bool firstRender)
|
||||||
|
{
|
||||||
|
if (!firstRender)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
return base.OnAfterRenderAsync(firstRender);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OpenAuditDialogAsync()
|
||||||
|
{
|
||||||
|
if (this.Plugin is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var parameters = new DialogParameters<AssistantPluginAuditDialog>
|
||||||
|
{
|
||||||
|
{ x => x.PluginId, this.Plugin.Id },
|
||||||
|
};
|
||||||
|
var dialog = await this.DialogService.ShowAsync<AssistantPluginAuditDialog>(this.T("Assistant Audit"), parameters, DialogOptions.FULLSCREEN);
|
||||||
|
var result = await dialog.Result;
|
||||||
|
if (result is null || result.Canceled || result.Data is not AssistantPluginAuditDialogResult auditResult)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (auditResult.Audit is not null)
|
||||||
|
UpsertAudit(this.SettingsManager.ConfigurationData.AssistantPluginAudits, auditResult.Audit);
|
||||||
|
|
||||||
|
if (auditResult.ActivatePlugin && !this.SettingsManager.ConfigurationData.EnabledPlugins.Contains(this.Plugin.Id))
|
||||||
|
this.SettingsManager.ConfigurationData.EnabledPlugins.Add(this.Plugin.Id);
|
||||||
|
|
||||||
|
await this.SettingsManager.StoreSettings();
|
||||||
|
await this.SendMessage(Event.CONFIGURATION_CHANGED, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
|
||||||
|
{
|
||||||
|
if (triggeredEvent is Event.CONFIGURATION_CHANGED or Event.PLUGINS_RELOADED)
|
||||||
|
return this.InvokeAsync(this.StateHasChanged);
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ToggleSecurityCard() => this.showSecurityCard = !this.showSecurityCard;
|
||||||
|
|
||||||
|
private void HideSecurityCard() => this.showSecurityCard = false;
|
||||||
|
|
||||||
|
private void ToggleDetails() => this.showDetails = !this.showDetails;
|
||||||
|
|
||||||
|
private static void UpsertAudit(List<PluginAssistantAudit> audits, PluginAssistantAudit audit)
|
||||||
|
{
|
||||||
|
var existingIndex = audits.FindIndex(x => x.PluginId == audit.PluginId);
|
||||||
|
if (existingIndex >= 0)
|
||||||
|
audits[existingIndex] = audit;
|
||||||
|
else
|
||||||
|
audits.Add(audit);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string FormatFileTimestamp(DateTime timestamp) => CommonTools.FormatTimestampToGeneral(timestamp, this.currentCultureInfo);
|
||||||
|
|
||||||
|
private string GetPopoverStyle() => $"border-color: {this.GetStatusBorderColor()};";
|
||||||
|
|
||||||
|
private double GetConfidencePercentage()
|
||||||
|
{
|
||||||
|
var confidence = this.SecurityState.Audit?.Confidence ?? 0f;
|
||||||
|
if (confidence <= 1)
|
||||||
|
confidence *= 100;
|
||||||
|
|
||||||
|
return Math.Clamp(confidence, 0, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetConfidenceLabel() => $"{this.GetConfidencePercentage():0}%";
|
||||||
|
|
||||||
|
private string GetFindingSummary()
|
||||||
|
{
|
||||||
|
var count = this.SecurityState.Audit?.Findings.Count ?? 0;
|
||||||
|
return string.Format(this.T("{0} Finding(s)"), count);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetAuditTimestampLabel()
|
||||||
|
{
|
||||||
|
var auditedAt = this.SecurityState.Audit?.AuditedAtUtc;
|
||||||
|
return auditedAt is null
|
||||||
|
? this.T("No audit yet")
|
||||||
|
: this.FormatFileTimestamp(auditedAt.Value.ToLocalTime().DateTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetAuditProviderLabel()
|
||||||
|
{
|
||||||
|
var providerName = this.SecurityState.Audit?.AuditProviderName;
|
||||||
|
return string.IsNullOrWhiteSpace(providerName) ? this.T("Unknown") : providerName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetShortHash(string hash)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(hash) || hash.Length <= 16)
|
||||||
|
return hash;
|
||||||
|
|
||||||
|
return $"{hash[..8]}...{hash[^8..]}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private Severity GetStatusSeverity() => this.SecurityState.AuditColor switch
|
||||||
|
{
|
||||||
|
Color.Success => Severity.Success,
|
||||||
|
Color.Warning => Severity.Warning,
|
||||||
|
Color.Error => Severity.Error,
|
||||||
|
_ => Severity.Info,
|
||||||
|
};
|
||||||
|
|
||||||
|
private string GetStatusBorderColor() => this.SecurityState.AuditColor switch
|
||||||
|
{
|
||||||
|
Color.Success => "var(--mud-palette-success)",
|
||||||
|
Color.Warning => "var(--mud-palette-warning)",
|
||||||
|
Color.Error => "var(--mud-palette-error)",
|
||||||
|
_ => "var(--mud-palette-info)",
|
||||||
|
};
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user