fix(management): canonicalize role-mapping casing at write path, closing UI/actor case split (arch-review C5)
Claude-Session: https://claude.ai/code/session_01MtdgwpEeCUn6cUA5f1LMPj
This commit is contained in:
@@ -109,9 +109,47 @@ public class RoleMappingValidationTests : TestKit, IDisposable
|
||||
|
||||
ExpectMsg<ManagementSuccess>(TimeSpan.FromSeconds(5));
|
||||
Assert.NotNull(inserted);
|
||||
// Membership check does not alter the stored value's casing (case-sensitivity
|
||||
// asymmetry is a separately-deferred change).
|
||||
// A correctly-cased canonical role round-trips unchanged.
|
||||
Assert.Equal(Roles.Deployer, inserted!.Role);
|
||||
Assert.Equal("SCADA-Deploy", inserted.LdapGroupName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateRoleMapping_LowercaseRole_StoresCanonicalCasing()
|
||||
{
|
||||
// arch-review C5: ASP.NET RequireClaim policies compare Role ordinal
|
||||
// case-sensitively while the actor compares OrdinalIgnoreCase. A row stored
|
||||
// as "deployer" would pass every actor gate but fail every UI policy (a
|
||||
// CLI-works/UI-403s split-brain). The write path now canonicalizes to the
|
||||
// Roles.All casing, so the stored value is always "Deployer".
|
||||
LdapGroupMapping? inserted = null;
|
||||
_securityRepo
|
||||
.When(r => r.AddMappingAsync(Arg.Any<LdapGroupMapping>(), Arg.Any<CancellationToken>()))
|
||||
.Do(ci => inserted = ci.Arg<LdapGroupMapping>());
|
||||
|
||||
var actor = CreateActor();
|
||||
actor.Tell(Envelope(new CreateRoleMappingCommand("SCADA-Deploy", "deployer")));
|
||||
|
||||
ExpectMsg<ManagementSuccess>(TimeSpan.FromSeconds(5));
|
||||
Assert.NotNull(inserted);
|
||||
Assert.Equal("Deployer", inserted!.Role); // canonical, not the verbatim "deployer"
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UpdateRoleMapping_LowercaseRole_StoresCanonicalCasing()
|
||||
{
|
||||
var existing = new LdapGroupMapping("SCADA-Deploy", Roles.Viewer) { Id = 7 };
|
||||
_securityRepo.GetMappingByIdAsync(7, Arg.Any<CancellationToken>()).Returns(existing);
|
||||
LdapGroupMapping? updated = null;
|
||||
_securityRepo
|
||||
.When(r => r.UpdateMappingAsync(Arg.Any<LdapGroupMapping>(), Arg.Any<CancellationToken>()))
|
||||
.Do(ci => updated = ci.Arg<LdapGroupMapping>());
|
||||
|
||||
var actor = CreateActor();
|
||||
actor.Tell(Envelope(new UpdateRoleMappingCommand(7, "SCADA-Deploy", "deployer")));
|
||||
|
||||
ExpectMsg<ManagementSuccess>(TimeSpan.FromSeconds(5));
|
||||
Assert.NotNull(updated);
|
||||
Assert.Equal("Deployer", updated!.Role);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user