Files
scadaproj/ZB.MOM.WW.Auth/src/ZB.MOM.WW.Auth.Ldap/Internal/LdapUserName.cs
T

58 lines
2.1 KiB
C#

namespace ZB.MOM.WW.Auth.Ldap.Internal;
/// <summary>
/// Reduces a typed login to the bare account name used by <see cref="Abstractions.Ldap.LdapBindMode.User"/>:
/// one spelling flows into the bind template, the search filter and the returned username, so audit
/// rows never carry <c>DOMAIN\jdoe</c> for one sign-in and <c>jdoe</c> for the next.
/// </summary>
internal static class LdapUserName
{
/// <summary>
/// Trims <paramref name="input"/> and, when <paramref name="acceptDomainQualified"/>, strips one
/// leading <c>DOMAIN\</c> or one trailing <c>@suffix</c>. Returns <see langword="false"/> (with
/// <paramref name="name"/> empty) for blank input, for both forms at once, for a repeated
/// separator, for an empty side, or for any qualified form when <paramref name="acceptDomainQualified"/>
/// is <see langword="false"/>.
/// </summary>
public static bool TryNormalize(string? input, bool acceptDomainQualified, out string name)
{
name = "";
if (string.IsNullOrWhiteSpace(input))
return false;
var s = input.Trim();
var slash = s.IndexOf('\\');
var at = s.IndexOf('@');
if (slash >= 0 || at >= 0)
{
if (!acceptDomainQualified || (slash >= 0 && at >= 0))
return false;
if (slash >= 0)
{
if (slash == 0 || s.IndexOf('\\', slash + 1) >= 0)
return false;
s = s[(slash + 1)..];
}
else
{
// Unlike the slash branch (whose extracted suffix is checked for emptiness by the
// final length check below), the extracted piece here is the *prefix* — so an empty
// domain part (a trailing "@", e.g. "jdoe@") must be rejected explicitly.
if (at == s.Length - 1 || s.IndexOf('@', at + 1) >= 0)
return false;
s = s[..at];
}
s = s.Trim();
}
if (s.Length == 0)
return false;
name = s;
return true;
}
}