feat(behandelportal): WP-62 medewerker caller identity + authz seam

Splits backend CallerIdentity into the two ADR-0002 §3 actor kinds
(ZorgverlenerCaller/MedewerkerCaller), a stub X-Medewerker/X-Rollen header
path mirroring WP-53's citizen stub, and Authz.CanBeoordelen as the first
medewerker capability — backend-only, no consumer until WP-64. Also fixes
the backlog README's stale WP-61 status (done, but table said todo).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
eho
2026-07-31 23:47:43 +02:00
co-authored by Claude Sonnet 5
parent e4ea75414b
commit a09c4ed87b
21 changed files with 289 additions and 72 deletions
@@ -46,7 +46,7 @@ public class ZgwTokenProviderTests
[Fact]
public void Mint_with_a_caller_carries_that_citizen_not_the_static_config_identity()
{
var caller = new CallerIdentity("111222333", "Dr. Citizen", PrincipalRole.Drafter);
var caller = new ZorgverlenerCaller("111222333", "Dr. Citizen", PrincipalRole.Drafter);
var token = new ZgwTokenProvider(Options).Mint(caller);
var payload = JsonSerializer.Deserialize<JsonElement>(Decode(token.Split('.')[1]));
@@ -56,6 +56,19 @@ public class ZgwTokenProviderTests
Assert.Equal("big-register", payload.GetProperty("client_id").GetString());
}
[Fact]
public void Mint_with_a_medewerker_caller_uses_the_medewerkerId_as_user_id()
{
// WP-62: SubjectId is what ZgwTokenProvider.Mint reads — a medewerker's is its
// medewerkerId, not a BSN, and this is the only place that's directly observable.
var caller = new MedewerkerCaller("m.jansen", [MedewerkerRol.Behandelaar], "M. Jansen", PrincipalRole.Drafter);
var token = new ZgwTokenProvider(Options).Mint(caller);
var payload = JsonSerializer.Deserialize<JsonElement>(Decode(token.Split('.')[1]));
Assert.Equal("m.jansen", payload.GetProperty("user_id").GetString());
Assert.Equal("M. Jansen", payload.GetProperty("user_representation").GetString());
}
[Fact]
public void Signature_verifies_with_the_shared_secret()
{