fix: make Admin LDAP sign-in work against GLAuth

Three bugs blocked sign-in entirely:

- Login.razor is static-SSR but its form model lacked
  [SupplyParameterFromForm], so the posted username/password never
  bound — SignInAsync saw empty fields and bailed before LDAP was
  contacted. Annotate the model; seed it in OnInitialized since
  BL0008 forbids an initializer on a [SupplyParameterFromForm]
  property.
- appsettings.json ServiceAccountDn used ou=svcaccts, which GLAuth
  reads as a (non-existent) group — the service-account bind failed
  with "Group not found". Use cn=serviceaccount,dc=lmxopcua,dc=local.
- LdapAuthService resolved the user DN by searching (uid=...), but
  GLAuth keys users by cn. Add an LdapOptions.UserNameAttribute knob
  (default cn for GLAuth; set sAMAccountName for Active Directory)
  and use it for the search filter.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-05-18 02:48:00 -04:00
parent 482d5f5637
commit 5f5bfe1ea5
4 changed files with 18 additions and 4 deletions

View File

@@ -108,7 +108,7 @@ public sealed class LdapAuthService(IOptions<LdapOptions> options, ILogger<LdapA
await Task.Run(() =>
conn.Bind(_options.ServiceAccountDn, _options.ServiceAccountPassword), ct);
var filter = $"(uid={EscapeLdapFilter(username)})";
var filter = $"({_options.UserNameAttribute}={EscapeLdapFilter(username)})";
var results = await Task.Run(() =>
conn.Search(_options.SearchBase, LdapConnection.ScopeSub, filter, ["dn"], false), ct);
@@ -116,7 +116,7 @@ public sealed class LdapAuthService(IOptions<LdapOptions> options, ILogger<LdapA
return results.Next().Dn;
throw new LdapException("User not found", LdapException.NoSuchObject,
$"No entry for uid={username}");
$"No entry for {filter}");
}
return string.IsNullOrWhiteSpace(_options.SearchBase)

View File

@@ -29,6 +29,13 @@ public sealed class LdapOptions
public string DisplayNameAttribute { get; set; } = "cn";
public string GroupAttribute { get; set; } = "memberOf";
/// <summary>
/// Attribute the service-account search matches the login name against to resolve the
/// user's DN. <c>cn</c> for GLAuth (the dev default); set <c>sAMAccountName</c> for
/// Active Directory.
/// </summary>
public string UserNameAttribute { get; set; } = "cn";
/// <summary>
/// Maps LDAP group name → Admin role. Group match is case-insensitive. A user gets every
/// role whose source group is in their membership list. Example dev mapping: