Files
ScadaBridge/src/ZB.MOM.WW.ScadaBridge.CentralUI/Services/ConfigurationCommandService.cs
T
2026-09-09 16:13:58 -04:00

58 lines
3.3 KiB
C#

using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.Extensions.DependencyInjection;
using ZB.MOM.WW.ScadaBridge.CentralUI.Auth;
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Services;
using ZB.MOM.WW.ScadaBridge.Commons.Messages.Management;
using ZB.MOM.WW.ScadaBridge.ManagementService;
using ZB.MOM.WW.ScadaBridge.Security;
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Services;
/// <summary>Fresh caller authorization and audited configuration commands for page event handlers.</summary>
public sealed class ConfigurationCommandService(IServiceProvider services, AuthenticationStateProvider auth)
{
/// <summary>Runs an existing audited instance operation with fresh scope and an atomic audit commit.</summary>
public async Task<T> ExecuteInstanceAsync<T>(int? instanceId, int? createSiteId,
Func<ZB.MOM.WW.ScadaBridge.TemplateEngine.Services.InstanceService, string, Task<T>> operation)
{
var username = await auth.RequireRoleAsync(Roles.Deployer);
var sites = new SiteScopeService(auth);
try
{
return (T)(await services.GetRequiredService<IConfigurationCommandTransaction>().ExecuteAsync(async sp =>
{
var siteId = createSiteId;
if (instanceId.HasValue)
{
var instance = await sp.GetRequiredService<ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories.ITemplateEngineRepository>().GetInstanceByIdAsync(instanceId.Value);
siteId = instance?.SiteId;
}
if (!siteId.HasValue || !await sites.IsSiteAllowedAsync(siteId.Value))
throw new UnauthorizedAccessException("Instance site access was revoked.");
return await operation(sp.GetRequiredService<ZB.MOM.WW.ScadaBridge.TemplateEngine.Services.InstanceService>(), username);
}))!;
}
finally { services.GetService<IConfigurationReadSession>()?.Reset(); }
}
/// <summary>Executes one command through the same guarded boundary as the management actor.</summary>
public async Task<object?> ExecuteAsync(object command)
{
var principal = (await auth.GetAuthoritativeStateAsync()).User;
if (principal.Identity?.IsAuthenticated != true) throw new UnauthorizedAccessException("Session expired.");
var user = new AuthenticatedUser(principal.GetUsername(), principal.GetDisplayName() ?? principal.GetUsername(),
principal.FindAll(JwtTokenService.RoleClaimType).Select(c => c.Value).ToArray(),
principal.FindAll(JwtTokenService.SiteIdClaimType).Select(c => c.Value).ToArray());
try { return await ManagementActor.ExecuteConfigurationCommandAsync(services, command, user, services.GetService<CircuitClientAddressProvider>()?.ClientAddress); }
finally { services.GetService<IConfigurationReadSession>()?.Reset(); }
}
}
/// <summary>Resolves the mutation facade only when a page acts, keeping read-only rendering independent.</summary>
public static class ConfigurationCommandExtensions
{
/// <summary>Submits a database-only configuration mutation with current server-side authority.</summary>
public static Task<object?> ExecuteConfigurationAsync(this IServiceProvider services, object command)
=> services.GetRequiredService<ConfigurationCommandService>().ExecuteAsync(command);
}