feat(auth): cut OtOpcUa over to ZB.MOM.WW.Auth.Ldap; preserve DevStubMode; route roles via IGroupRoleMapper (Task 1.2/1.4)

This commit is contained in:
Joseph Doherty
2026-06-02 00:55:10 -04:00
parent 6534875476
commit 257caa7bd1
14 changed files with 495 additions and 274 deletions
@@ -1,3 +1,6 @@
using ZB.MOM.WW.Auth.Abstractions.Ldap;
using LibLdapOptions = ZB.MOM.WW.Auth.Abstractions.Ldap.LdapOptions;
namespace ZB.MOM.WW.OtOpcUa.Security.Ldap;
/// <summary>
@@ -5,6 +8,14 @@ namespace ZB.MOM.WW.OtOpcUa.Security.Ldap;
/// <c>Security:Ldap</c> section. Defaults point at the local GLAuth dev instance (see
/// <c>C:\publish\glauth\auth.md</c>).
/// </summary>
/// <remarks>
/// Carries both the wire fields the shared <c>ZB.MOM.WW.Auth.Ldap</c> directory client needs
/// (<see cref="Server"/>/<see cref="Port"/>/<see cref="Transport"/>/…) AND the app-only concerns
/// the shared library has no notion of (<see cref="Enabled"/> master switch,
/// <see cref="DevStubMode"/> dev bypass, <see cref="GroupToRole"/> appsettings role baseline).
/// The app wrapper (<c>OtOpcUaLdapAuthService</c>) projects this onto the library's
/// <see cref="LibLdapOptions"/> at construction; see <see cref="ToLibraryOptions"/>.
/// </remarks>
public sealed class LdapOptions
{
public const string SectionName = "Security:Ldap";
@@ -18,14 +29,21 @@ public sealed class LdapOptions
/// <summary>Gets or sets the LDAP server port.</summary>
public int Port { get; set; } = 3893;
/// <summary>Gets or sets a value indicating whether to use TLS for LDAP connection.</summary>
public bool UseTls { get; set; }
/// <summary>
/// Transport security for the LDAP connection — <see cref="LdapTransport.Ldaps"/> (implicit
/// TLS), <see cref="LdapTransport.StartTls"/> (upgrade), or <see cref="LdapTransport.None"/>
/// (plaintext, dev/test only — requires <see cref="AllowInsecure"/>). Replaces the former
/// <c>UseTls</c> bool (Task 1.4): <c>true</c>→<see cref="LdapTransport.Ldaps"/>,
/// <c>false</c>→<see cref="LdapTransport.None"/>.
/// </summary>
public LdapTransport Transport { get; set; } = LdapTransport.None;
/// <summary>Dev-only escape hatch — must be <c>false</c> in production.</summary>
public bool AllowInsecureLdap { get; set; }
/// <summary>Dev-only escape hatch — must be <c>false</c> in production. Maps to the shared
/// library's <see cref="LibLdapOptions.AllowInsecure"/> (renamed from <c>AllowInsecureLdap</c>).</summary>
public bool AllowInsecure { get; set; }
/// <summary>
/// Dev-only stub: when <c>true</c>, <see cref="LdapAuthService"/> bypasses the real LDAP
/// Dev-only stub: when <c>true</c>, <see cref="OtOpcUaLdapAuthService"/> bypasses the real LDAP
/// bind and accepts any non-empty username/password, returning a single FleetAdmin role
/// so the operator can navigate the full Admin UI. MUST be <c>false</c> in production.
/// </summary>
@@ -62,4 +80,26 @@ public sealed class LdapOptions
/// <code>"ReadOnly":"ConfigViewer","ReadWrite":"ConfigEditor","AlarmAck":"FleetAdmin"</code>
/// </summary>
public Dictionary<string, string> GroupToRole { get; set; } = new(StringComparer.OrdinalIgnoreCase);
/// <summary>
/// Projects the wire fields onto the shared <c>ZB.MOM.WW.Auth.Ldap</c>
/// <see cref="LibLdapOptions"/> the directory client consumes. App-only concerns
/// (<see cref="DevStubMode"/>, <see cref="GroupToRole"/>) have no library counterpart and are
/// handled by the app wrapper around the library service; <see cref="Enabled"/> is carried
/// through so the library's own feature gate stays consistent with the app master switch.
/// </summary>
public LibLdapOptions ToLibraryOptions() => new()
{
Enabled = Enabled,
Server = Server,
Port = Port,
Transport = Transport,
AllowInsecure = AllowInsecure,
SearchBase = SearchBase,
ServiceAccountDn = ServiceAccountDn,
ServiceAccountPassword = ServiceAccountPassword,
UserNameAttribute = UserNameAttribute,
DisplayNameAttribute = DisplayNameAttribute,
GroupAttribute = GroupAttribute,
};
}