58 lines
3.3 KiB
C#
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);
|
|
}
|