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:
@@ -167,12 +167,99 @@ public class ApiKeyCreationTests : TestKit, IDisposable
|
||||
var response = ExpectMsg<ManagementSuccess>(TimeSpan.FromSeconds(5));
|
||||
|
||||
Assert.Equal(new[] { "New1", "New2", "New3" }, _admin.Keys["key-1"].Methods);
|
||||
Assert.Equal("true", response.JsonData);
|
||||
|
||||
// Fix 3 (review): response is now the richer { KeyId, Methods } shape.
|
||||
using var doc = JsonDocument.Parse(response.JsonData);
|
||||
Assert.Equal("key-1", doc.RootElement.GetProperty("keyId").GetString());
|
||||
var methods = doc.RootElement.GetProperty("methods").EnumerateArray()
|
||||
.Select(m => m.GetString()).ToArray();
|
||||
Assert.Equal(new[] { "New1", "New2", "New3" }, methods);
|
||||
|
||||
_auditService.Received(1).LogAsync(
|
||||
"admin", "Update", "ApiKey", "key-1", Arg.Any<string>(), Arg.Any<object?>());
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Fix 1 (review): not-found on mutating ops → ManagementError, no audit
|
||||
// =========================================================================
|
||||
|
||||
[Fact]
|
||||
public void UpdateApiKey_UnknownKey_ReturnsManagementError_AndDoesNotAudit()
|
||||
{
|
||||
// No keys seeded — "key-unknown" does not exist.
|
||||
var actor = CreateActor();
|
||||
actor.Tell(Envelope(new UpdateApiKeyCommand("key-unknown", false), "Admin"));
|
||||
|
||||
var response = ExpectMsg<ManagementError>(TimeSpan.FromSeconds(5));
|
||||
|
||||
Assert.Contains("key-unknown", response.Error);
|
||||
// Seam returned false → audit must not have fired.
|
||||
_auditService.DidNotReceive().LogAsync(
|
||||
Arg.Any<string>(), "Update", "ApiKey", Arg.Any<string>(), Arg.Any<string>(), Arg.Any<object?>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetApiKeyMethods_UnknownKey_ReturnsManagementError_AndDoesNotAudit()
|
||||
{
|
||||
var actor = CreateActor();
|
||||
actor.Tell(Envelope(new SetApiKeyMethodsCommand("key-unknown", new[] { "M1" }), "Admin"));
|
||||
|
||||
var response = ExpectMsg<ManagementError>(TimeSpan.FromSeconds(5));
|
||||
|
||||
Assert.Contains("key-unknown", response.Error);
|
||||
_auditService.DidNotReceive().LogAsync(
|
||||
Arg.Any<string>(), "Update", "ApiKey", Arg.Any<string>(), Arg.Any<string>(), Arg.Any<object?>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DeleteApiKey_UnknownKey_ReturnsManagementError_AndDoesNotAudit()
|
||||
{
|
||||
var actor = CreateActor();
|
||||
actor.Tell(Envelope(new DeleteApiKeyCommand("key-unknown"), "Admin"));
|
||||
|
||||
var response = ExpectMsg<ManagementError>(TimeSpan.FromSeconds(5));
|
||||
|
||||
Assert.Contains("key-unknown", response.Error);
|
||||
_auditService.DidNotReceive().LogAsync(
|
||||
Arg.Any<string>(), "Delete", "ApiKey", Arg.Any<string>(), Arg.Any<string>(), Arg.Any<object?>());
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Fix 2 (review): empty methods set is rejected before seam + audit
|
||||
// =========================================================================
|
||||
|
||||
[Fact]
|
||||
public void CreateApiKey_EmptyMethods_ReturnsManagementError()
|
||||
{
|
||||
var actor = CreateActor();
|
||||
actor.Tell(Envelope(new CreateApiKeyCommand("MES-Production", Array.Empty<string>()), "Admin"));
|
||||
|
||||
var response = ExpectMsg<ManagementError>(TimeSpan.FromSeconds(5));
|
||||
|
||||
Assert.Contains("method", response.Error, StringComparison.OrdinalIgnoreCase);
|
||||
// No key should have been created.
|
||||
Assert.Empty(_admin.Keys);
|
||||
_auditService.DidNotReceive().LogAsync(
|
||||
Arg.Any<string>(), "Create", "ApiKey", Arg.Any<string>(), Arg.Any<string>(), Arg.Any<object?>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetApiKeyMethods_EmptyMethods_ReturnsManagementError()
|
||||
{
|
||||
_admin.Seed("key-1", "Service A", enabled: true, "M1");
|
||||
|
||||
var actor = CreateActor();
|
||||
actor.Tell(Envelope(new SetApiKeyMethodsCommand("key-1", Array.Empty<string>()), "Admin"));
|
||||
|
||||
var response = ExpectMsg<ManagementError>(TimeSpan.FromSeconds(5));
|
||||
|
||||
Assert.Contains("method", response.Error, StringComparison.OrdinalIgnoreCase);
|
||||
// Existing scope set must be unchanged.
|
||||
Assert.Equal(new[] { "M1" }, _admin.Keys["key-1"].Methods);
|
||||
_auditService.DidNotReceive().LogAsync(
|
||||
Arg.Any<string>(), "Update", "ApiKey", Arg.Any<string>(), Arg.Any<string>(), Arg.Any<object?>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(AllApiKeyCommands))]
|
||||
public void EveryApiKeyCommand_RequiresAdminRole(object command)
|
||||
|
||||
Reference in New Issue
Block a user