feat(auth)!: ScadaBridge retire SQL Server ApiKey entity + ApprovedApiKeyIds + legacy hashing; EF migration RetireInboundApiKeyStore; re-issue runbook + CHANGELOG (re-arch C5/E) — BREAKING: X-API-Key -> Bearer sbk_, keys re-issued

This commit is contained in:
Joseph Doherty
2026-06-02 05:39:59 -04:00
parent b13d7b3d28
commit afa55981d5
32 changed files with 2117 additions and 1193 deletions
@@ -1,11 +1,6 @@
using System.Net;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using Microsoft.Extensions.DependencyInjection;
using NSubstitute;
using ZB.MOM.WW.ScadaBridge.Commons.Entities.InboundApi;
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories;
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Services;
using ZB.MOM.WW.ScadaBridge.InboundAPI;
@@ -16,40 +11,11 @@ namespace ZB.MOM.WW.ScadaBridge.IntegrationTests;
/// </summary>
public class IntegrationSurfaceTests
{
// ── Inbound API: auth + routing + parameter validation + error codes ──
[Fact]
public async Task InboundAPI_ApiKeyValidator_FullFlow_EndToEnd()
{
// Validates that ApiKeyValidator correctly chains all checks.
var repository = Substitute.For<IInboundApiRepository>();
var key = new ApiKey("test-key", "key-value-123") { Id = 1, IsEnabled = true };
var method = new ApiMethod("getStatus", "return 1;")
{
Id = 10,
ParameterDefinitions = "[{\"Name\":\"deviceId\",\"Type\":\"String\",\"Required\":true}]",
TimeoutSeconds = 30
};
// ConfigurationDatabase-012: the validator fetches every key and matches
// the candidate by HMAC hash in constant time (no secret-equality lookup).
repository.GetAllApiKeysAsync().Returns(new List<ApiKey> { key });
repository.GetMethodByNameAsync("getStatus").Returns(method);
repository.GetApprovedKeysForMethodAsync(10).Returns(new List<ApiKey> { key });
var validator = new ApiKeyValidator(repository);
// Valid key + approved method
var result = await validator.ValidateAsync("key-value-123", "getStatus");
Assert.True(result.IsValid);
Assert.Equal(method, result.Method);
// Then validate parameters
using var doc = JsonDocument.Parse("{\"deviceId\": \"pump-01\"}");
var paramResult = ParameterValidator.Validate(doc.RootElement.Clone(), method.ParameterDefinitions);
Assert.True(paramResult.IsValid);
Assert.Equal("pump-01", paramResult.Parameters["deviceId"]);
}
// ── Inbound API: parameter validation + error codes ──
// Auth re-arch (C5): the in-repo ApiKeyValidator full-flow case was removed
// with the entity. Inbound auth now runs through the shared
// ZB.MOM.WW.Auth.ApiKeys verifier (Bearer sbk_<keyId>_<secret>); its coverage
// lives in the InboundAPI endpoint + Auth-library tests.
[Fact]
public void InboundAPI_ParameterValidation_ExtendedTypes()