Add LDAP authentication with role-based OPC UA permissions
Replace static user list with GLAuth LDAP authentication. Group membership (ReadOnly, ReadWrite, AlarmAck) maps to granular OPC UA permissions for write and alarm-ack operations. Anonymous can still browse and read but not write. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Opc.Ua;
|
||||
@@ -29,6 +30,7 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.OpcUa
|
||||
private readonly RedundancyConfiguration _redundancyConfig;
|
||||
private readonly string? _applicationUri;
|
||||
private readonly ServiceLevelCalculator _serviceLevelCalculator = new ServiceLevelCalculator();
|
||||
private readonly ConcurrentDictionary<string, IReadOnlyList<string>> _userAppRoles = new ConcurrentDictionary<string, IReadOnlyList<string>>(StringComparer.OrdinalIgnoreCase);
|
||||
private LmxNodeManager? _nodeManager;
|
||||
|
||||
/// <summary>
|
||||
@@ -36,6 +38,22 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.OpcUa
|
||||
/// </summary>
|
||||
public LmxNodeManager? NodeManager => _nodeManager;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the application-level roles cached for the given username, or null if no roles are stored.
|
||||
/// Called by LmxNodeManager to enforce per-role write and alarm-ack permissions.
|
||||
/// </summary>
|
||||
public IReadOnlyList<string>? GetUserAppRoles(string? username)
|
||||
{
|
||||
if (username != null && _userAppRoles.TryGetValue(username, out var roles))
|
||||
return roles;
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether LDAP role-based access control is active.
|
||||
/// </summary>
|
||||
public bool LdapRolesEnabled => _authProvider is Domain.IRoleProvider;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of active OPC UA sessions currently connected to the server.
|
||||
/// </summary>
|
||||
@@ -69,7 +87,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.OpcUa
|
||||
{
|
||||
var namespaceUri = $"urn:{_galaxyName}:LmxOpcUa";
|
||||
_nodeManager = new LmxNodeManager(server, configuration, namespaceUri, _mxAccessClient, _metrics,
|
||||
_historianDataSource, _alarmTrackingEnabled, _authConfig.AnonymousCanWrite);
|
||||
_historianDataSource, _alarmTrackingEnabled, _authConfig.AnonymousCanWrite,
|
||||
LdapRolesEnabled ? (Func<string?, IReadOnlyList<string>?>)GetUserAppRoles : null,
|
||||
LdapRolesEnabled);
|
||||
|
||||
var nodeManagers = new List<INodeManager> { _nodeManager };
|
||||
return new MasterNodeManager(server, configuration, null, nodeManagers.ToArray());
|
||||
@@ -206,10 +226,23 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.OpcUa
|
||||
throw new ServiceResultException(StatusCodes.BadUserAccessDenied, "Invalid username or password");
|
||||
}
|
||||
|
||||
var roles = new List<Role> { Role.AuthenticatedUser };
|
||||
|
||||
// Resolve LDAP-based roles when the provider supports it
|
||||
if (_authProvider is Domain.IRoleProvider roleProvider)
|
||||
{
|
||||
var appRoles = roleProvider.GetUserRoles(userNameToken.UserName);
|
||||
_userAppRoles[userNameToken.UserName] = appRoles;
|
||||
Log.Information("User {Username} authenticated with roles [{Roles}]",
|
||||
userNameToken.UserName, string.Join(", ", appRoles));
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Information("User {Username} authenticated", userNameToken.UserName);
|
||||
}
|
||||
|
||||
args.Identity = new RoleBasedIdentity(
|
||||
new UserIdentity(userNameToken),
|
||||
new List<Role> { Role.AuthenticatedUser });
|
||||
Log.Information("User {Username} authenticated", userNameToken.UserName);
|
||||
new UserIdentity(userNameToken), roles);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user