fix(management-service): resolve ManagementService-005,008,010,011 — supervision strategy, configured command timeout, remove stale ResolveRoles path; ManagementService-012 deferred
This commit is contained in:
@@ -43,6 +43,22 @@ public class ManagementActor : ReceiveActor
|
||||
Receive<ManagementEnvelope>(HandleEnvelope);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the supervision strategy for <see cref="ManagementActor"/>. Per the project's
|
||||
/// Akka.NET conventions, coordinator-style actors use a Resume-based strategy so a faulted
|
||||
/// child preserves its state rather than restarting. <see cref="ManagementActor"/> spawns
|
||||
/// no children today, but declaring the strategy explicitly matches the convention and
|
||||
/// makes the contract correct ahead of any future worker actors (finding
|
||||
/// ManagementService-005).
|
||||
/// </summary>
|
||||
public static SupervisorStrategy CreateSupervisorStrategy() =>
|
||||
new OneForOneStrategy(
|
||||
maxNrOfRetries: -1,
|
||||
withinTimeRange: System.Threading.Timeout.InfiniteTimeSpan,
|
||||
decider: Decider.From(_ => Directive.Resume));
|
||||
|
||||
protected override SupervisorStrategy SupervisorStrategy() => CreateSupervisorStrategy();
|
||||
|
||||
/// <summary>
|
||||
/// Serializer settings for command results. <see cref="ReferenceHandler.IgnoreCycles"/>
|
||||
/// keeps EF-backed entity graphs with bidirectional navigation properties from throwing;
|
||||
@@ -304,29 +320,16 @@ public class ManagementActor : ReceiveActor
|
||||
DiscardParkedMessageCommand cmd => await HandleDiscardParkedMessage(sp, cmd, user),
|
||||
DebugSnapshotCommand cmd => await HandleDebugSnapshot(sp, cmd, user),
|
||||
|
||||
// Role resolution (for CLI LDAP auth)
|
||||
ResolveRolesCommand cmd => await HandleResolveRoles(sp, cmd),
|
||||
|
||||
// NOTE: ResolveRolesCommand is intentionally NOT dispatched. The two-step
|
||||
// "ResolveRoles + command" flow is retired — the HTTP endpoint performs LDAP
|
||||
// auth and role resolution itself before sending a single envelope. Leaving a
|
||||
// handler would expose role-mapping data to any raw ClusterClient sender with
|
||||
// no role requirement; the command now falls through to the default below
|
||||
// (finding ManagementService-011).
|
||||
_ => throw new NotSupportedException($"Unknown command type: {command.GetType().Name}")
|
||||
};
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// Role resolution
|
||||
// ========================================================================
|
||||
|
||||
private static async Task<object?> HandleResolveRoles(IServiceProvider sp, ResolveRolesCommand cmd)
|
||||
{
|
||||
var roleMapper = new RoleMapper(sp.GetRequiredService<ISecurityRepository>());
|
||||
var result = await roleMapper.MapGroupsToRolesAsync(cmd.LdapGroups);
|
||||
return new
|
||||
{
|
||||
Roles = result.Roles,
|
||||
PermittedSiteIds = result.PermittedSiteIds,
|
||||
IsSystemWideDeployment = result.IsSystemWideDeployment
|
||||
};
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// Site-scope enforcement
|
||||
// ========================================================================
|
||||
|
||||
@@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using ScadaLink.Commons.Messages.Management;
|
||||
using ScadaLink.Security;
|
||||
|
||||
@@ -13,7 +14,20 @@ namespace ScadaLink.ManagementService;
|
||||
|
||||
public static class ManagementEndpoints
|
||||
{
|
||||
private static readonly TimeSpan AskTimeout = TimeSpan.FromSeconds(30);
|
||||
private static readonly TimeSpan DefaultAskTimeout = TimeSpan.FromSeconds(30);
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the ManagementActor Ask timeout from configuration
|
||||
/// (finding ManagementService-010). Falls back to <see cref="DefaultAskTimeout"/>
|
||||
/// when options are absent or the configured value is not strictly positive — a
|
||||
/// zero/negative timeout would make every management call fail immediately.
|
||||
/// </summary>
|
||||
public static TimeSpan ResolveAskTimeout(ManagementServiceOptions? options)
|
||||
{
|
||||
if (options is { CommandTimeout: { Ticks: > 0 } configured })
|
||||
return configured;
|
||||
return DefaultAskTimeout;
|
||||
}
|
||||
|
||||
public static IEndpointRouteBuilder MapManagementAPI(this IEndpointRouteBuilder endpoints)
|
||||
{
|
||||
@@ -101,10 +115,13 @@ public static class ManagementEndpoints
|
||||
var correlationId = Guid.NewGuid().ToString("N");
|
||||
var envelope = new ManagementEnvelope(authenticatedUser, command, correlationId);
|
||||
|
||||
var askTimeout = ResolveAskTimeout(
|
||||
context.RequestServices.GetService<IOptions<ManagementServiceOptions>>()?.Value);
|
||||
|
||||
object response;
|
||||
try
|
||||
{
|
||||
response = await holder.ActorRef.Ask(envelope, AskTimeout);
|
||||
response = await holder.ActorRef.Ask(envelope, askTimeout);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user