namespace ZB.MOM.WW.MxGateway.Server.Dashboard;
///
/// Single source of truth for mapping a user's LDAP groups to dashboard roles.
/// Both (the existing login flow) and
/// (the shared-Auth
/// seam)
/// delegate here so the precedence and case rules stay identical.
///
internal static class DashboardGroupRoleMapping
{
///
/// Maps the user's LDAP groups to dashboard roles. A user can pick up
/// multiple roles; Admin and Viewer are the only legal values. Returns
/// an empty list when no group matches (caller rejects the login).
///
/// The collection of LDAP groups the user belongs to.
/// The mapping from group names to dashboard role names.
internal static IReadOnlyList MapGroupsToRoles(
IEnumerable groups,
IReadOnlyDictionary groupToRole)
{
if (groupToRole.Count == 0)
{
return [];
}
HashSet roles = new(StringComparer.Ordinal);
foreach (string group in groups)
{
string normalizedGroup = group.Trim();
// Lookup precedence (Server-040): the full literal group string is
// tried first; only if that misses do we fall back to the leading
// RDN value (e.g. "GwAdmin" extracted from
// "ou=GwAdmin,ou=groups,..."). The map's comparer is
// OrdinalIgnoreCase (see DashboardOptions.GroupToRole), so e.g.
// "GwAdmin" and "gwadmin" both match.
if (groupToRole.TryGetValue(normalizedGroup, out string? mapped)
|| groupToRole.TryGetValue(ExtractFirstRdnValue(normalizedGroup), out mapped))
{
roles.Add(mapped);
}
}
return [.. roles];
}
/// Extracts the first RDN value from a distinguished name.
/// The LDAP distinguished name.
internal static string ExtractFirstRdnValue(string distinguishedName)
{
int equalsIndex = distinguishedName.IndexOf('=');
if (equalsIndex < 0)
{
return distinguishedName;
}
int valueStart = equalsIndex + 1;
int commaIndex = distinguishedName.IndexOf(',', valueStart);
return commaIndex > valueStart
? distinguishedName[valueStart..commaIndex]
: distinguishedName[valueStart..];
}
}