feat(auth): add IGroupRoleMapper<string> seam (Task 1.1)
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
using Microsoft.Extensions.Options;
|
||||
using ZB.MOM.WW.Auth.Abstractions.Roles;
|
||||
using ZB.MOM.WW.MxGateway.Server.Configuration;
|
||||
using ZB.MOM.WW.MxGateway.Server.Dashboard;
|
||||
|
||||
namespace ZB.MOM.WW.MxGateway.Tests.Gateway.Dashboard;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for <see cref="DashboardGroupRoleMapper"/>, the shared-Auth
|
||||
/// <see cref="IGroupRoleMapper{TRole}"/> seam over the dashboard's
|
||||
/// LDAP-group → role mapping. Behaviour must match the existing
|
||||
/// <c>DashboardAuthenticator.MapGroupsToRoles</c> precedence/case rules.
|
||||
/// </summary>
|
||||
public sealed class DashboardGroupRoleMapperTests
|
||||
{
|
||||
private static DashboardGroupRoleMapper CreateMapper(Dictionary<string, string> mapping)
|
||||
{
|
||||
GatewayOptions options = new()
|
||||
{
|
||||
Dashboard = new DashboardOptions
|
||||
{
|
||||
GroupToRole = mapping,
|
||||
},
|
||||
};
|
||||
|
||||
return new DashboardGroupRoleMapper(Options.Create(options));
|
||||
}
|
||||
|
||||
private static Dictionary<string, string> StandardMapping() => new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
["GwAdmin"] = DashboardRoles.Admin,
|
||||
["GwReader"] = DashboardRoles.Viewer,
|
||||
};
|
||||
|
||||
/// <summary>Verifies full-DN match, leading-RDN fallback, case-insensitivity, and unmapped → empty.</summary>
|
||||
/// <param name="ldapGroup">The LDAP group name or distinguished name.</param>
|
||||
/// <param name="expectedRole">The expected role or null if no match.</param>
|
||||
[Theory]
|
||||
[InlineData("GwAdmin", DashboardRoles.Admin)]
|
||||
[InlineData("gwadmin", DashboardRoles.Admin)]
|
||||
[InlineData("ou=GwAdmin,ou=groups,dc=lmxopcua,dc=local", DashboardRoles.Admin)]
|
||||
[InlineData("OtherGroup", null)]
|
||||
public async Task MapAsync_ResolvesByShortNameAndDistinguishedName(
|
||||
string ldapGroup,
|
||||
string? expectedRole)
|
||||
{
|
||||
DashboardGroupRoleMapper mapper = CreateMapper(StandardMapping());
|
||||
|
||||
GroupRoleMapping<string> result = await mapper.MapAsync([ldapGroup], CancellationToken.None);
|
||||
|
||||
if (expectedRole is null)
|
||||
{
|
||||
Assert.Empty(result.Roles);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Equal(expectedRole, Assert.Single(result.Roles));
|
||||
}
|
||||
|
||||
Assert.Null(result.Scope);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that admin and viewer roles are both emitted when both groups are present.</summary>
|
||||
[Fact]
|
||||
public async Task MapAsync_AdminPlusViewer_BothRolesEmitted()
|
||||
{
|
||||
DashboardGroupRoleMapper mapper = CreateMapper(StandardMapping());
|
||||
|
||||
GroupRoleMapping<string> result = await mapper.MapAsync(
|
||||
["GwAdmin", "GwReader"],
|
||||
CancellationToken.None);
|
||||
|
||||
Assert.Contains(DashboardRoles.Admin, result.Roles);
|
||||
Assert.Contains(DashboardRoles.Viewer, result.Roles);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that an empty GroupToRole map yields no roles.</summary>
|
||||
[Fact]
|
||||
public async Task MapAsync_EmptyMapping_ReturnsNoRoles()
|
||||
{
|
||||
DashboardGroupRoleMapper mapper = CreateMapper(new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase));
|
||||
|
||||
GroupRoleMapping<string> result = await mapper.MapAsync(["GwAdmin"], CancellationToken.None);
|
||||
|
||||
Assert.Empty(result.Roles);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies the extracted shared helper is the single source of truth: it
|
||||
/// produces the same roles the mapper does for the same inputs.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task SharedHelper_MatchesMapperOutput()
|
||||
{
|
||||
Dictionary<string, string> mapping = StandardMapping();
|
||||
DashboardGroupRoleMapper mapper = CreateMapper(mapping);
|
||||
string[] groups = ["ou=GwAdmin,ou=groups,dc=lmxopcua,dc=local", "gwreader"];
|
||||
|
||||
IReadOnlyList<string> helperRoles = DashboardGroupRoleMapping.MapGroupsToRoles(groups, mapping);
|
||||
GroupRoleMapping<string> mapperResult = await mapper.MapAsync(groups, CancellationToken.None);
|
||||
|
||||
Assert.Equal([.. helperRoles.OrderBy(r => r)], [.. mapperResult.Roles.OrderBy(r => r)]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user