using Microsoft.Extensions.Logging; using ZB.MOM.WW.OtOpcUa.OpcUaServer.Security; using ZB.MOM.WW.OtOpcUa.Security.Ldap; namespace ZB.MOM.WW.OtOpcUa.Host.OpcUa; /// /// Production adapter that bridges OPC UA UserName /// tokens to the same the Admin UI cookie/JWT flows use, so a /// single LDAP source-of-truth governs both control-plane (Admin) and data-plane (OPC UA) /// session identities. Roles flow through unchanged — the data-plane ACL evaluator reads /// them off OperationContext.UserIdentity downstream. /// public sealed class LdapOpcUaUserAuthenticator( ILdapAuthService ldap, ILogger logger) : IOpcUaUserAuthenticator { public async Task AuthenticateUserNameAsync(string username, string password, CancellationToken ct) { try { var result = await ldap.AuthenticateAsync(username, password, ct).ConfigureAwait(false); if (!result.Success) { return OpcUaUserAuthResult.Deny(result.Error ?? "Invalid credentials"); } return OpcUaUserAuthResult.Allow(result.DisplayName ?? username, result.Roles); } catch (Exception ex) when (ex is not OperationCanceledException) { logger.LogWarning(ex, "LDAP authentication threw for OPC UA user {User}", username); return OpcUaUserAuthResult.Deny("Authentication backend error"); } } }