fix(auth): C2 review — not-found throws (no spurious audit) on update/delete/set-methods, reject empty methods (unusable-key/stealth-disable), richer set-methods response, token advisory to stderr

This commit is contained in:
Joseph Doherty
2026-06-02 04:21:28 -04:00
parent 6518e93424
commit 8219b8ee18
4 changed files with 188 additions and 5 deletions
@@ -1315,6 +1315,9 @@ public class ManagementActor : ReceiveActor
// peppered hash, and assembles the one-time bearer token (sbk_<keyId>_<secret>).
// The token is shown to the operator only here, in the create response; it cannot
// be retrieved later. No hash/secret is stored or returned by ScadaBridge.
if (cmd.Methods is null || cmd.Methods.Count == 0)
throw new ManagementCommandException("At least one method must be specified for an API key.");
var admin = sp.GetRequiredService<IInboundApiKeyAdmin>();
var created = await admin.CreateAsync(cmd.Name, cmd.Methods);
@@ -1333,6 +1336,8 @@ public class ManagementActor : ReceiveActor
{
var admin = sp.GetRequiredService<IInboundApiKeyAdmin>();
var deleted = await admin.DeleteAsync(cmd.KeyId);
if (!deleted)
throw new ManagementCommandException($"API key '{cmd.KeyId}' not found.");
await AuditAsync(sp, user, "Delete", "ApiKey", cmd.KeyId, cmd.KeyId, null);
return deleted;
}
@@ -1757,7 +1762,9 @@ public class ManagementActor : ReceiveActor
{
// Inbound-API key re-arch (C2): enable/disable via the shared seam (no secret change).
var admin = sp.GetRequiredService<IInboundApiKeyAdmin>();
await admin.SetEnabledAsync(cmd.KeyId, cmd.IsEnabled);
var updated = await admin.SetEnabledAsync(cmd.KeyId, cmd.IsEnabled);
if (!updated)
throw new ManagementCommandException($"API key '{cmd.KeyId}' not found.");
await AuditAsync(sp, user, "Update", "ApiKey", cmd.KeyId, cmd.KeyId,
new { cmd.KeyId, cmd.IsEnabled });
return new { cmd.KeyId, cmd.IsEnabled };
@@ -1767,11 +1774,16 @@ public class ManagementActor : ReceiveActor
{
// Inbound-API key re-arch (C2): replace a key's method-scope set via the shared seam
// (no secret change). The library is authoritative for the scope replacement.
if (cmd.Methods is null || cmd.Methods.Count == 0)
throw new ManagementCommandException("At least one method must be specified for an API key.");
var admin = sp.GetRequiredService<IInboundApiKeyAdmin>();
var updated = await admin.SetMethodsAsync(cmd.KeyId, cmd.Methods);
if (!updated)
throw new ManagementCommandException($"API key '{cmd.KeyId}' not found.");
await AuditAsync(sp, user, "Update", "ApiKey", cmd.KeyId, cmd.KeyId,
new { cmd.KeyId, cmd.Methods });
return updated;
return new { cmd.KeyId, Methods = cmd.Methods };
}
private static async Task<object?> HandleListScopeRules(IServiceProvider sp, ListScopeRulesCommand cmd)