Add authentication and role-based write access control
Implements configurable user authentication (anonymous + username/password) with pluggable credential provider (IUserAuthenticationProvider). Anonymous writes can be disabled via AnonymousCanWrite setting while reads remain open. Adds -U/-P flags to all CLI commands for authenticated sessions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,7 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.OpcUa
|
||||
private readonly PerformanceMetrics _metrics;
|
||||
private readonly HistorianDataSource? _historianDataSource;
|
||||
private readonly bool _alarmTrackingEnabled;
|
||||
private readonly bool _anonymousCanWrite;
|
||||
private readonly string _namespaceUri;
|
||||
|
||||
// NodeId → full_tag_reference for read/write resolution
|
||||
@@ -190,7 +191,8 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.OpcUa
|
||||
IMxAccessClient mxAccessClient,
|
||||
PerformanceMetrics metrics,
|
||||
HistorianDataSource? historianDataSource = null,
|
||||
bool alarmTrackingEnabled = false)
|
||||
bool alarmTrackingEnabled = false,
|
||||
bool anonymousCanWrite = true)
|
||||
: base(server, configuration, namespaceUri)
|
||||
{
|
||||
_namespaceUri = namespaceUri;
|
||||
@@ -198,6 +200,7 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.OpcUa
|
||||
_metrics = metrics;
|
||||
_historianDataSource = historianDataSource;
|
||||
_alarmTrackingEnabled = alarmTrackingEnabled;
|
||||
_anonymousCanWrite = anonymousCanWrite;
|
||||
|
||||
// Wire up data change delivery
|
||||
_mxAccessClient.OnTagValueChanged += OnMxAccessDataChange;
|
||||
@@ -378,7 +381,7 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.OpcUa
|
||||
new LocalizedText("en", alarmAttr.AttributeName + " Alarm"),
|
||||
true);
|
||||
condition.SourceNode.Value = sourceNodeId;
|
||||
condition.SourceName.Value = alarmAttr.AttributeName;
|
||||
condition.SourceName.Value = alarmAttr.FullTagReference.TrimEnd('[', ']');
|
||||
condition.ConditionName.Value = alarmAttr.AttributeName;
|
||||
condition.AutoReportStateChanges = true;
|
||||
|
||||
@@ -833,7 +836,7 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.OpcUa
|
||||
new LocalizedText("en", alarmAttr.AttributeName + " Alarm"),
|
||||
true);
|
||||
condition.SourceNode.Value = sourceNodeId;
|
||||
condition.SourceName.Value = alarmAttr.AttributeName;
|
||||
condition.SourceName.Value = alarmAttr.FullTagReference.TrimEnd('[', ']');
|
||||
condition.ConditionName.Value = alarmAttr.AttributeName;
|
||||
condition.AutoReportStateChanges = true;
|
||||
condition.SetEnableState(SystemContext, true);
|
||||
@@ -1100,6 +1103,14 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.OpcUa
|
||||
if (errors[i] != null && errors[i].StatusCode == StatusCodes.BadNotWritable)
|
||||
continue;
|
||||
|
||||
// Enforce role-based write access: reject anonymous writes when AnonymousCanWrite is false
|
||||
if (!_anonymousCanWrite && context.UserIdentity?.GrantedRoleIds != null &&
|
||||
!context.UserIdentity.GrantedRoleIds.Contains(ObjectIds.WellKnownRole_AuthenticatedUser))
|
||||
{
|
||||
errors[i] = new ServiceResult(StatusCodes.BadUserAccessDenied);
|
||||
continue;
|
||||
}
|
||||
|
||||
var nodeId = nodesToWrite[i].NodeId;
|
||||
if (nodeId.NamespaceIndex != NamespaceIndex) continue;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user