feat(auth): LdapBindMode.User options and validation

This commit is contained in:
Joseph Doherty
2026-10-01 00:47:04 -04:00
parent 9ac6ed51d3
commit 711e8beb2b
4 changed files with 125 additions and 3 deletions
@@ -4,6 +4,20 @@ namespace ZB.MOM.WW.Auth.Abstractions.Ldap;
public enum LdapTransport { Ldaps, StartTls, None }
/// <summary>How <see cref="LdapOptions"/> authenticates a sign-in.</summary>
public enum LdapBindMode
{
/// <summary>Bind as the service account, search for the user, re-bind as the user (0.3.0 behaviour).</summary>
ServiceAccount,
/// <summary>
/// Bind as the signing-in user (identity built from <see cref="LdapOptions.UserBindTemplate"/>);
/// a successful bind authenticates them, and the same connection reads their own entry for groups.
/// No service account is used.
/// </summary>
User,
}
public sealed record LdapOptions
{
public bool Enabled { get; init; } = true;
@@ -19,6 +33,27 @@ public sealed record LdapOptions
public string GroupAttribute { get; init; } = "memberOf";
public int ConnectionTimeoutMs { get; init; } = 10_000;
/// <summary>
/// Selects the authentication flow. <see cref="LdapBindMode.ServiceAccount"/> (the default) is the
/// 0.3.0 bind-then-search-then-bind flow; <see cref="LdapBindMode.User"/> binds as the user and needs
/// no <see cref="ServiceAccountDn"/> / <see cref="ServiceAccountPassword"/>.
/// </summary>
public LdapBindMode BindMode { get; init; } = LdapBindMode.ServiceAccount;
/// <summary>
/// <see cref="LdapBindMode.User"/> only: the simple-bind identity, with <c>{0}</c> replaced by the
/// normalised login name — e.g. <c>{0}@example.com</c> (UPN) or <c>EXAMPLE\{0}</c>. Must contain
/// <c>{0}</c> exactly once.
/// </summary>
public string UserBindTemplate { get; init; } = "";
/// <summary>
/// <see cref="LdapBindMode.User"/> only: when <see langword="true"/> (default) a login typed as
/// <c>DOMAIN\name</c> or <c>name@suffix</c> is reduced to <c>name</c>; when <see langword="false"/>
/// such input is refused as bad credentials without contacting the directory.
/// </summary>
public bool AcceptDomainQualifiedNames { get; init; } = true;
/// <summary>
/// Ordered fallback LDAP endpoints tried when the primary (and any earlier fallback)
/// fails with a system-side error (connect/TLS, service-account bind, or search).