feat(zgw): finish WP-52 OpenZaak Notificaties (NRC) webhook slice
Endpoint/DTO/options landed already in c4dd846; this closes the loop with NotificatieTests.cs (accept/reject/missing-header, asserting the AuthzAuditStore row), missing appsettings.json keys (also backfills DrcBaseUrl/ InformatieobjecttypeUrls, stale since WP-51), and the webhook + abonnement provisioning docs. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"_Zgw": "WP-49/50: set Enabled=true + the URLs/credentials/RSINs/zaaktype map to source + create cases against a real OpenZaak. Off = local SQLite store (offline POC default).",
|
||||
"_Zgw": "WP-49..52: set Enabled=true + the URLs/credentials/RSINs/type maps to source, create and document cases against a real OpenZaak; NrcBaseUrl/NotificatieAuthorization configure the inbound notificaties webhook. Off = local SQLite store (offline POC default).",
|
||||
"Zgw": {
|
||||
"Enabled": false,
|
||||
"ZrcBaseUrl": "",
|
||||
@@ -21,6 +21,10 @@
|
||||
"registratie": "",
|
||||
"herregistratie": "",
|
||||
"intake": ""
|
||||
}
|
||||
},
|
||||
"DrcBaseUrl": "",
|
||||
"InformatieobjecttypeUrls": {},
|
||||
"NrcBaseUrl": "",
|
||||
"NotificatieAuthorization": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using BigRegister.Api.Contracts;
|
||||
using BigRegister.Api.Zgw;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
|
||||
namespace BigRegister.Tests;
|
||||
|
||||
/// WP-52: the inbound Notificaties (NRC) webhook — auth accept/reject + the audit trail it
|
||||
/// writes via AuthzAuditStore (no Principal exists for an NRC caller, so this doesn't go
|
||||
/// through the Principal-shaped AuditAuthz helper the user-facing endpoints use).
|
||||
public class NotificatieTests(TestWebApplicationFactory factory) : IClassFixture<TestWebApplicationFactory>
|
||||
{
|
||||
private readonly HttpClient _client = factory.CreateClient();
|
||||
|
||||
private static NotificatieDto Sample(string zaakUrl) => new(
|
||||
Kanaal: "zaken",
|
||||
HoofdObject: zaakUrl,
|
||||
Resource: "zaak",
|
||||
ResourceUrl: zaakUrl,
|
||||
Actie: "update",
|
||||
Aanmaakdatum: DateTimeOffset.UtcNow,
|
||||
Kenmerken: null);
|
||||
|
||||
private async Task<HttpResponseMessage> Post(string? authorization, string zaakUrl)
|
||||
{
|
||||
var req = new HttpRequestMessage(HttpMethod.Post, "/api/v1/zgw/notificaties")
|
||||
{
|
||||
Content = JsonContent.Create(Sample(zaakUrl)),
|
||||
};
|
||||
if (authorization is not null) req.Headers.TryAddWithoutValidation("Authorization", authorization);
|
||||
return await _client.SendAsync(req);
|
||||
}
|
||||
|
||||
private async Task<List<AuthzAuditDto>> AuditLog()
|
||||
{
|
||||
var req = new HttpRequestMessage(HttpMethod.Get, "/api/v1/admin/audit");
|
||||
req.Headers.Add("X-Role", "admin");
|
||||
var res = await _client.SendAsync(req);
|
||||
res.EnsureSuccessStatusCode();
|
||||
return (await res.Content.ReadFromJsonAsync<List<AuthzAuditDto>>())!;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Correct_shared_secret_is_accepted_and_recorded()
|
||||
{
|
||||
var zaak = $"https://open-zaak.example/zaken/api/v1/zaken/{Guid.NewGuid()}";
|
||||
var res = await Post("test-nrc-secret", zaak);
|
||||
|
||||
Assert.Equal(HttpStatusCode.NoContent, res.StatusCode);
|
||||
Assert.Contains(await AuditLog(), e =>
|
||||
e.Action == "zgw:notificatie" && e.Resource == zaak && e.Decision == "allow" && e.Role == "nrc");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Wrong_secret_is_rejected_and_recorded()
|
||||
{
|
||||
var zaak = $"https://open-zaak.example/zaken/api/v1/zaken/{Guid.NewGuid()}";
|
||||
var res = await Post("not-the-secret", zaak);
|
||||
|
||||
Assert.Equal(HttpStatusCode.Unauthorized, res.StatusCode);
|
||||
Assert.Contains(await AuditLog(), e =>
|
||||
e.Action == "zgw:notificatie" && e.Resource == zaak && e.Decision == "deny");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Missing_authorization_header_is_rejected()
|
||||
{
|
||||
var zaak = $"https://open-zaak.example/zaken/api/v1/zaken/{Guid.NewGuid()}";
|
||||
Assert.Equal(HttpStatusCode.Unauthorized, (await Post(null, zaak)).StatusCode);
|
||||
}
|
||||
}
|
||||
@@ -27,8 +27,11 @@ public sealed class TestWebApplicationFactory : WebApplicationFactory<Program>
|
||||
{
|
||||
private readonly string _dbPath = Path.Combine(Path.GetTempPath(), $"bigregister-test-{Guid.NewGuid():N}.db");
|
||||
|
||||
protected override void ConfigureWebHost(IWebHostBuilder builder) =>
|
||||
builder.UseSetting("ConnectionStrings:AppDb", $"Data Source={_dbPath}");
|
||||
protected override void ConfigureWebHost(IWebHostBuilder builder) => builder
|
||||
.UseSetting("ConnectionStrings:AppDb", $"Data Source={_dbPath}")
|
||||
// WP-52: a fixed shared secret so NotificatieTests can exercise the accept path —
|
||||
// the appsettings.json default is "" (reject everything), which no test should rely on.
|
||||
.UseSetting("Zgw:NotificatieAuthorization", "test-nrc-secret");
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user