Skip to content

Commit

Permalink
Optimize GetNodeMetadata (#1392)
Browse files Browse the repository at this point in the history
* Optimized GetNodeMetadata to support reading AccessRestrictions and RolePermission attributes when specified; All service calls use only these attributes on validating AccessRestrictions and RolePermissions. Read and Browse services also cache the values for avoiding unnecessary calls.
  • Loading branch information
mrsuciu committed May 5, 2021
1 parent 244b26e commit 8b9bbe2
Show file tree
Hide file tree
Showing 3 changed files with 251 additions and 22 deletions.
134 changes: 131 additions & 3 deletions Libraries/Opc.Ua.Server/Diagnostics/CustomNodeManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1047,9 +1047,9 @@ public virtual void AddReferences(IDictionary<NodeId, IList<IReference>> referen
/// This method validates any placeholder handle.
/// </remarks>
public virtual NodeMetadata GetNodeMetadata(
OperationContext context,
object targetHandle,
BrowseResultMask resultMask)
OperationContext context,
object targetHandle,
BrowseResultMask resultMask)
{
ServerSystemContext systemContext = m_systemContext.Copy(context);

Expand Down Expand Up @@ -1195,6 +1195,62 @@ public virtual void AddReferences(IDictionary<NodeId, IList<IReference>> referen
}
}

/// <summary>
/// Sets the AccessRestrictions, RolePermissions and UserRolePermissions values in the metadata
/// </summary>
/// <param name="values"></param>
/// <param name="metadata"></param>
private static void SetAccessAndRolePermissions(List<object> values, NodeMetadata metadata)
{
if (values[0] != null)
{
metadata.AccessRestrictions = (AccessRestrictionType)Enum.ToObject(typeof(AccessRestrictionType), values[0]);
}
if (values[1] != null)
{
metadata.RolePermissions = new RolePermissionTypeCollection(ExtensionObject.ToList<RolePermissionType>(values[1]));
}
if (values[2] != null)
{
metadata.UserRolePermissions = new RolePermissionTypeCollection(ExtensionObject.ToList<RolePermissionType>(values[2]));
}
}

/// <summary>
/// Reads and caches the Attributes used by the AccessRestrictions and RolePermission validation process
/// </summary>
/// <param name="uniqueNodesServiceAttributes">The cache used to save the attributes</param>
/// <param name="systemContext">The context</param>
/// <param name="target">The target for which the attributes are read and cached</param>
/// <param name="key">The key representing the NodeId for which the cache is kept</param>
/// <returns>The values of the attributes</returns>
private static List<object> ReadAndCacheValidationAttributes(Dictionary<NodeId, List<object>> uniqueNodesServiceAttributes, ServerSystemContext systemContext, NodeState target, NodeId key)
{
List<object> values = ReadValidationAttributes(systemContext, target);
uniqueNodesServiceAttributes[key] = values;

return values;
}

/// <summary>
/// Reads the Attributes used by the AccessRestrictions and RolePermission validation process
/// </summary>
/// <param name="systemContext">The context</param>
/// <param name="target">The target for which the attributes are read and cached</param>
/// <returns>The values of the attributes</returns>
private static List<object> ReadValidationAttributes(ServerSystemContext systemContext, NodeState target)
{
// This is the list of attributes to be populated by GetNodeMetadata from CustomNodeManagers.
// The are originating from services in the context of AccessRestrictions and RolePermission validation.
// For such calls the other attributes are ignored since reading them might trigger unnecessary callbacks
List<object> values = target.ReadAttributes(systemContext,
Attributes.AccessRestrictions,
Attributes.RolePermissions,
Attributes.UserRolePermissions);

return values;
}

/// <summary>
/// Browses the references from a node managed by the node manager.
/// </summary>
Expand Down Expand Up @@ -4300,6 +4356,78 @@ public virtual bool IsNodeInView(OperationContext context, NodeId viewId, object

return false;
}

/// <summary>
/// Returns the metadata containing the AccessRestrictions, RolePermissions and UserRolePermissions for the node.
/// Returns null if the node does not exist.
/// </summary>
/// <remarks>
/// This method validates any placeholder handle.
/// </remarks>
public virtual NodeMetadata GetPermissionMetadata(
OperationContext context,
object targetHandle,
BrowseResultMask resultMask,
Dictionary<NodeId, List<object>> uniqueNodesServiceAttributes,
bool permissionsOnly)
{
ServerSystemContext systemContext = m_systemContext.Copy(context);

lock (Lock)
{
// check for valid handle.
NodeHandle handle = IsHandleInNamespace(targetHandle);

if (handle == null)
{
return null;
}

// validate node.
NodeState target = ValidateNode(systemContext, handle, null);

if (target == null)
{
return null;
}

List<object> values = null;

// construct the meta-data object.
NodeMetadata metadata = new NodeMetadata(target, target.NodeId);

// Treat the case of calls originating from the optimized services that use the cache (Read, Browse and Call services)
if (uniqueNodesServiceAttributes != null)
{
NodeId key = handle.NodeId;
if (uniqueNodesServiceAttributes.ContainsKey(key))
{
if (uniqueNodesServiceAttributes[key].Count == 0)
{
values = ReadAndCacheValidationAttributes(uniqueNodesServiceAttributes, systemContext, target, key);
}
else
{
// Retrieve value from cache
values = uniqueNodesServiceAttributes[key];
}
}
else
{
values = ReadAndCacheValidationAttributes(uniqueNodesServiceAttributes, systemContext, target, key);
}

SetAccessAndRolePermissions(values, metadata);
}// All other calls that do not use the cache
else if (permissionsOnly == true)
{
values = ReadValidationAttributes(systemContext, target);
SetAccessAndRolePermissions(values, metadata);
}

return metadata;
}
}
#endregion

#region ComponentCache Functions
Expand Down
21 changes: 18 additions & 3 deletions Libraries/Opc.Ua.Server/NodeManager/INodeManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public interface INodeManager
bool isInverse,
ExpandedNodeId targetId,
bool deleteBidirectional);

/// <summary>
/// Returns the metadata associated with the node.
/// </summary>
Expand All @@ -105,9 +105,9 @@ public interface INodeManager
/// </remarks>
NodeMetadata GetNodeMetadata(
OperationContext context,
object targetHandle,
object targetHandle,
BrowseResultMask resultMask);

/// <summary>
/// Returns the set of references that meet the filter criteria.
/// </summary>
Expand Down Expand Up @@ -312,6 +312,21 @@ public interface INodeManager2 : INodeManager
/// Returns true if the node is in the view.
/// </summary>
bool IsNodeInView(OperationContext context, NodeId viewId, object nodeHandle);

/// <summary>
/// Returns the metadata needed for validating permissions, associated with the node with
/// the option to optimize services by using a cache.
/// </summary>
/// <remarks>
/// Returns null if the node does not exist.
/// It should return null in case the implementation wishes to handover the task to the parent INodeManager.GetNodeMetadata
/// </remarks>
NodeMetadata GetPermissionMetadata(
OperationContext context,
object targetHandle,
BrowseResultMask resultMask,
Dictionary<NodeId, List<object>> uniqueNodesServiceAttributesCache,
bool permissionsOnly);
}

/// <summary>
Expand Down

0 comments on commit 8b9bbe2

Please sign in to comment.