Skip to content

Commit 8db0032

Browse files
committed
Add INodeManager3 with role permission validation
Introduce INodeManager3 interface extending INodeManager2, adding synchronous role permission validation methods. Update all node manager interfaces and classes to use INodeManager3. Extend IAsyncNodeManager with async permission validation methods and update adapters for sync/async bridging. Deprecate FindNodeInAddressSpace in favor of new async FindNodeInAddressSpaceAsync. Refactor MonitoredNode2 to use thread-safe collections and locking for monitored items. Enhance event reporting with security and permission checks. Update constructors and documentation to reflect new interfaces and best practices.
1 parent 2aec6a6 commit 8db0032

12 files changed

+206
-148
lines changed

Libraries/Opc.Ua.Server/Configuration/ConfigurationNodeManager.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ protected override NodeState AddBehaviourToPredefinedNode(
184184
}
185185
else
186186
{
187-
NodeState serverNode = FindNodeInAddressSpace(ObjectIds.Server);
187+
NodeState serverNode = Server.NodeManager.FindNodeInAddressSpaceAsync(ObjectIds.Server).AsTask().GetAwaiter().GetResult();
188188
serverNode?.ReplaceChild(context, activeNode);
189189
}
190190
// remove the reference to server node because it is set as parent
@@ -1192,8 +1192,8 @@ private NamespaceMetadataState FindNamespaceMetadataState(string namespaceUri)
11921192
var nameSpaceNodeId = ExpandedNodeId.ToNodeId(
11931193
serverNamespacesReference.TargetId,
11941194
Server.NamespaceUris);
1195-
if (FindNodeInAddressSpace(
1196-
nameSpaceNodeId) is not NamespaceMetadataState namespaceMetadata)
1195+
if (Server.NodeManager.FindNodeInAddressSpaceAsync(
1196+
nameSpaceNodeId).AsTask().GetAwaiter().GetResult() is not NamespaceMetadataState namespaceMetadata)
11971197
{
11981198
continue;
11991199
}

Libraries/Opc.Ua.Server/Configuration/IConfigurationNodeManager.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ namespace Opc.Ua.Server
3232
/// <summary>
3333
/// The Server Configuration Node Manager.
3434
/// </summary>
35-
public interface IConfigurationNodeManager : INodeManager2
35+
public interface IConfigurationNodeManager : INodeManager3
3636
{
3737
/// <summary>
3838
/// Gets or creates the <see cref="NamespaceMetadataState"/> node for the specified NamespaceUri.

Libraries/Opc.Ua.Server/Diagnostics/IDiagnosticsNodeManager.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ namespace Opc.Ua.Server
3232
/// <summary>
3333
/// A node manager the diagnostic information exposed by the server.
3434
/// </summary>
35-
public interface IDiagnosticsNodeManager : INodeManager2, INodeIdFactory
35+
public interface IDiagnosticsNodeManager : INodeManager3, INodeIdFactory
3636
{
3737
/// <summary>
3838
/// True if diagnostics are currently enabled.

Libraries/Opc.Ua.Server/NodeManager/Adapters/AsyncNodeManagerAdapter.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,38 @@ public ValueTask WriteAsync(
606606
return default;
607607
}
608608

609+
/// <inheritdoc/>
610+
public ValueTask<ServiceResult> ValidateEventRolePermissionsAsync(IEventMonitoredItem monitoredItem, IFilterTarget filterTarget)
611+
{
612+
if (SyncNodeManager is IAsyncNodeManager asyncNodeManager)
613+
{
614+
return asyncNodeManager.ValidateEventRolePermissionsAsync(monitoredItem, filterTarget);
615+
}
616+
617+
if (SyncNodeManager is INodeManager3 nodeManager2)
618+
{
619+
return new ValueTask<ServiceResult>(nodeManager2.ValidateEventRolePermissions(monitoredItem, filterTarget));
620+
}
621+
622+
return new ValueTask<ServiceResult>(ServiceResult.Good);
623+
}
624+
625+
/// <inheritdoc/>
626+
public ValueTask<ServiceResult> ValidateRolePermissionsAsync(OperationContext operationContext, NodeId nodeId, PermissionType requestedPermission)
627+
{
628+
if (SyncNodeManager is IAsyncNodeManager asyncNodeManager)
629+
{
630+
return asyncNodeManager.ValidateRolePermissionsAsync(operationContext, nodeId, requestedPermission);
631+
}
632+
633+
if (SyncNodeManager is INodeManager3 nodeManager2)
634+
{
635+
return new ValueTask<ServiceResult>(nodeManager2.ValidateRolePermissions(operationContext, nodeId, requestedPermission));
636+
}
637+
638+
return new ValueTask<ServiceResult>(ServiceResult.Good);
639+
}
640+
609641
/// <summary>
610642
/// Frees any unmanaged resources.
611643
/// </summary>

Libraries/Opc.Ua.Server/NodeManager/Adapters/SyncNodeManagerAdapter.cs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ public static class SyncNodeManagerAdapterFactory
4242
/// if the NodeManager does not implement the interface uses the <see cref="SyncNodeManagerAdapter"/>
4343
/// to create an ISyncNodeManager compatible object
4444
/// </summary>
45-
public static INodeManager2 ToSyncNodeManager(this IAsyncNodeManager nodeManager)
45+
public static INodeManager3 ToSyncNodeManager(this IAsyncNodeManager nodeManager)
4646
{
47-
if (nodeManager is INodeManager2 syncNodeManager)
47+
if (nodeManager is INodeManager3 syncNodeManager)
4848
{
4949
return syncNodeManager;
5050
}
@@ -59,7 +59,7 @@ public static INodeManager2 ToSyncNodeManager(this IAsyncNodeManager nodeManager
5959
/// This allows asynchronous nodeManagers to be treated as synchronous, which can help
6060
/// compatibility with existing code.
6161
/// </remarks>
62-
public class SyncNodeManagerAdapter : INodeManager2
62+
public class SyncNodeManagerAdapter : INodeManager3
6363
{
6464
/// <summary>
6565
/// Initializes a new instance of the <see cref="SyncNodeManagerAdapter"/> class.
@@ -288,6 +288,18 @@ public NodeMetadata GetPermissionMetadata(
288288
.AsTask().GetAwaiter().GetResult();
289289
}
290290

291+
/// <inheritdoc/>
292+
public ServiceResult ValidateEventRolePermissions(IEventMonitoredItem monitoredItem, IFilterTarget filterTarget)
293+
{
294+
return m_nodeManager.ValidateEventRolePermissionsAsync(monitoredItem, filterTarget).AsTask().GetAwaiter().GetResult();
295+
}
296+
297+
/// <inheritdoc/>
298+
public ServiceResult ValidateRolePermissions(OperationContext operationContext, NodeId nodeId, PermissionType requestedPermission)
299+
{
300+
return m_nodeManager.ValidateRolePermissionsAsync(operationContext, nodeId, requestedPermission).AsTask().GetAwaiter().GetResult();
301+
}
302+
291303
private readonly IAsyncNodeManager m_nodeManager;
292304
}
293305
}

Libraries/Opc.Ua.Server/NodeManager/CustomNodeManager.cs

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ namespace Opc.Ua.Server
4848
/// is not part of the SDK because most real implementations of a INodeManager will need to
4949
/// modify the behavior of the base class.
5050
/// </remarks>
51-
public partial class CustomNodeManager2 : INodeManager2, INodeIdFactory, IDisposable
51+
public partial class CustomNodeManager2 : INodeManager3, INodeIdFactory, IDisposable
5252
{
5353
/// <summary>
5454
/// Initializes the node manager.
@@ -169,7 +169,7 @@ protected CustomNodeManager2(
169169
}
170170
else
171171
{
172-
m_monitoredItemManager = new MonitoredNodeMonitoredItemManager(this);
172+
m_monitoredItemManager = new MonitoredNodeMonitoredItemManager(this, server);
173173
}
174174

175175
PredefinedNodes = [];
@@ -445,22 +445,10 @@ public bool DeleteNode(ServerSystemContext context, NodeId nodeId)
445445
/// <summary>
446446
/// Searches the node id in all node managers
447447
/// </summary>
448+
[Obsolete("Use IServerInteral.IMasterNodeManager.FindNodeInAddressSpaceAsync instead.")]
448449
public NodeState FindNodeInAddressSpace(NodeId nodeId)
449450
{
450-
if (nodeId.IsNull)
451-
{
452-
return null;
453-
}
454-
// search node id in all node managers
455-
foreach (INodeManager nodeManager in Server.NodeManager.NodeManagers)
456-
{
457-
if (nodeManager.GetManagerHandle(nodeId) is not NodeHandle handle)
458-
{
459-
continue;
460-
}
461-
return handle.Node;
462-
}
463-
return null;
451+
return Server.NodeManager.FindNodeInAddressSpaceAsync(nodeId).AsTask().GetAwaiter().GetResult();
464452
}
465453

466454
/// <summary>

Libraries/Opc.Ua.Server/NodeManager/IMasterNodeManager.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,12 @@ ValueTask DeleteMonitoredItemsAsync(
141141
/// </summary>
142142
ValueTask DeleteReferencesAsync(NodeId targetId, IList<IReference> references, CancellationToken cancellationToken = default);
143143

144+
/// <summary>
145+
/// Searches the node id in all node managers,
146+
/// returns the node state if found (and node Manager supports it), otherwise returns null.
147+
/// </summary>
148+
ValueTask<NodeState> FindNodeInAddressSpaceAsync(NodeId nodeId);
149+
144150
/// <summary>
145151
/// Returns node handle and its node manager.
146152
/// </summary>

Libraries/Opc.Ua.Server/NodeManager/INodeManager.cs

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,28 @@ NodeMetadata GetPermissionMetadata(
376376
}
377377

378378
/// <summary>
379-
/// An asynchronous version of the "Call" method defined on the <see cref="INodeManager2"/> interface.
379+
/// An interface to an object that manages a set of nodes in the address space.
380+
/// </summary>
381+
public interface INodeManager3 : INodeManager2
382+
{
383+
/// <summary>
384+
/// Validates if the specified event monitored item has enough permissions to receive the specified event
385+
/// </summary>
386+
ServiceResult ValidateEventRolePermissions(
387+
IEventMonitoredItem monitoredItem,
388+
IFilterTarget filterTarget);
389+
390+
/// <summary>
391+
/// Validates Role permissions for the specified NodeId
392+
/// </summary>
393+
ServiceResult ValidateRolePermissions(
394+
OperationContext operationContext,
395+
NodeId nodeId,
396+
PermissionType requestedPermission);
397+
}
398+
399+
/// <summary>
400+
/// An asynchronous version of the "Call" method defined on the <see cref="INodeManager3"/> interface.
380401
/// </summary>
381402
public interface ICallAsyncNodeManager
382403
{
@@ -392,7 +413,7 @@ ValueTask CallAsync(
392413
}
393414

394415
/// <summary>
395-
/// An asynchronous version of the "Read" method defined on the <see cref="INodeManager2"/> interface.
416+
/// An asynchronous version of the "Read" method defined on the <see cref="INodeManager3"/> interface.
396417
/// </summary>
397418
public interface IReadAsyncNodeManager
398419
{
@@ -427,7 +448,7 @@ ValueTask ReadAsync(
427448
}
428449

429450
/// <summary>
430-
/// An asynchronous version of the "Write" method defined on the <see cref="INodeManager2"/> interface.
451+
/// An asynchronous version of the "Write" method defined on the <see cref="INodeManager3"/> interface.
431452
/// </summary>
432453
public interface IWriteAsyncNodeManager
433454
{
@@ -446,7 +467,7 @@ ValueTask WriteAsync(
446467
}
447468

448469
/// <summary>
449-
/// An asynchronous version of the "HistoryRead" method defined on the <see cref="INodeManager2"/> interface.
470+
/// An asynchronous version of the "HistoryRead" method defined on the <see cref="INodeManager3"/> interface.
450471
/// </summary>
451472
public interface IHistoryReadAsyncNodeManager
452473
{
@@ -465,7 +486,7 @@ ValueTask HistoryReadAsync(
465486
}
466487

467488
/// <summary>
468-
/// An asynchronous version of the "HistoryUpdate" method defined on the <see cref="INodeManager2"/> interface.
489+
/// An asynchronous version of the "HistoryUpdate" method defined on the <see cref="INodeManager3"/> interface.
469490
/// </summary>
470491
public interface IHistoryUpdateAsyncNodeManager
471492
{
@@ -482,7 +503,7 @@ ValueTask HistoryUpdateAsync(
482503
}
483504

484505
/// <summary>
485-
/// An asynchronous version of the "ConditionRefresh" method defined on the <see cref="INodeManager2"/> interface.
506+
/// An asynchronous version of the "ConditionRefresh" method defined on the <see cref="INodeManager3"/> interface.
486507
/// </summary>
487508
public interface IConditionRefreshAsyncNodeManager
488509
{
@@ -496,7 +517,7 @@ ValueTask ConditionRefreshAsync(
496517
}
497518

498519
/// <summary>
499-
/// An asynchronous version of the "TranslateBrowsePath" method defined on the <see cref="INodeManager2"/> interface.
520+
/// An asynchronous version of the "TranslateBrowsePath" method defined on the <see cref="INodeManager3"/> interface.
500521
/// </summary>
501522
public interface ITranslateBrowsePathAsyncNodeManager
502523
{
@@ -527,7 +548,7 @@ ValueTask TranslateBrowsePathAsync(
527548
}
528549

529550
/// <summary>
530-
/// An asynchronous version of the "Browse" method defined on the <see cref="INodeManager2"/> interface.
551+
/// An asynchronous version of the "Browse" method defined on the <see cref="INodeManager3"/> interface.
531552
/// </summary>
532553
public interface IBrowseAsyncNodeManager
533554
{
@@ -557,7 +578,7 @@ ValueTask<ContinuationPoint> BrowseAsync(
557578
}
558579

559580
/// <summary>
560-
/// An asynchronous version of the "SetMonitoringMode" method defined on the <see cref="INodeManager2"/> interface.
581+
/// An asynchronous version of the "SetMonitoringMode" method defined on the <see cref="INodeManager3"/> interface.
561582
/// </summary>
562583
public interface ISetMonitoringModeAsyncNodeManager
563584
{
@@ -574,7 +595,7 @@ ValueTask SetMonitoringModeAsync(
574595
}
575596

576597
/// <summary>
577-
/// An asynchronous version of the "TransferMonitoredItems" method defined on the <see cref="INodeManager2"/> interface.
598+
/// An asynchronous version of the "TransferMonitoredItems" method defined on the <see cref="INodeManager3"/> interface.
578599
/// </summary>
579600
public interface ITransferMonitoredItemsAsyncNodeManager
580601
{
@@ -594,7 +615,7 @@ ValueTask TransferMonitoredItemsAsync(
594615
}
595616

596617
/// <summary>
597-
/// An asynchronous version of the "DeleteMonitoredItems" method defined on the <see cref="INodeManager2"/> interface.
618+
/// An asynchronous version of the "DeleteMonitoredItems" method defined on the <see cref="INodeManager3"/> interface.
598619
/// </summary>
599620
public interface IDeleteMonitoredItemsAsyncNodeManager
600621
{
@@ -610,7 +631,7 @@ ValueTask DeleteMonitoredItemsAsync(
610631
}
611632

612633
/// <summary>
613-
/// An asynchronous version of the "ModifyMonitoredItems" method defined on the <see cref="INodeManager2"/> interface.
634+
/// An asynchronous version of the "ModifyMonitoredItems" method defined on the <see cref="INodeManager3"/> interface.
614635
/// </summary>
615636
public interface IModifyMonitoredItemsAsyncNodeManager
616637
{
@@ -628,7 +649,7 @@ ValueTask ModifyMonitoredItemsAsync(
628649
}
629650

630651
/// <summary>
631-
/// An asynchronous version of the "CreateMonitoredItems" method defined on the <see cref="INodeManager2"/> interface.
652+
/// An asynchronous version of the "CreateMonitoredItems" method defined on the <see cref="INodeManager3"/> interface.
632653
/// </summary>
633654
public interface ICreateMonitoredItemsAsyncNodeManager
634655
{
@@ -672,11 +693,10 @@ ValueTask<IAsyncNodeManager> CreateAsync(
672693
}
673694

674695
/// <summary>
675-
/// An asynchronous verison of the <see cref="INodeManager2"/> interface.
696+
/// An asynchronous verison of the <see cref="INodeManager3"/> interface.
676697
/// This interface is in active development and will be extended in future releases.
677698
/// Please use the sub interfaces to implement async support for specific service calls.
678699
/// </summary>
679-
[Experimental("UA_NETStandard_1")]
680700
public interface IAsyncNodeManager :
681701
ICallAsyncNodeManager,
682702
IReadAsyncNodeManager,
@@ -847,6 +867,21 @@ ValueTask RestoreMonitoredItemsAsync(
847867
IList<IMonitoredItem> monitoredItems,
848868
IUserIdentity savedOwnerIdentity,
849869
CancellationToken cancellationToken = default);
870+
871+
/// <summary>
872+
/// Validates if the specified event monitored item has enough permissions to receive the specified event
873+
/// </summary>
874+
ValueTask <ServiceResult> ValidateEventRolePermissionsAsync(
875+
IEventMonitoredItem monitoredItem,
876+
IFilterTarget filterTarget);
877+
878+
/// <summary>
879+
/// Validates Role permissions for the specified NodeId
880+
/// </summary>
881+
ValueTask<ServiceResult> ValidateRolePermissionsAsync(
882+
OperationContext operationContext,
883+
NodeId nodeId,
884+
PermissionType requestedPermission);
850885
}
851886

852887
/// <summary>

Libraries/Opc.Ua.Server/NodeManager/MasterNodeManager.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1794,6 +1794,26 @@ private async ValueTask<bool> UpdateReferenceDescriptionAsync(
17941794
return true;
17951795
}
17961796

1797+
/// <inheritdoc/>
1798+
public async ValueTask<NodeState> FindNodeInAddressSpaceAsync(NodeId nodeId)
1799+
{
1800+
if (nodeId.IsNull)
1801+
{
1802+
return null;
1803+
}
1804+
// search node id in all node managers
1805+
foreach (IAsyncNodeManager nodeManager in AsyncNodeManagers)
1806+
{
1807+
if ((await nodeManager.GetManagerHandleAsync(nodeId).ConfigureAwait(false))
1808+
is not NodeHandle handle)
1809+
{
1810+
continue;
1811+
}
1812+
return handle.Node;
1813+
}
1814+
return null;
1815+
}
1816+
17971817
/// <inheritdoc/>
17981818
public virtual async ValueTask<(DataValueCollection values, DiagnosticInfoCollection diagnosticInfos)> ReadAsync(
17991819
OperationContext context,

0 commit comments

Comments
 (0)