diff --git a/CHANGELOG b/CHANGELOG
index 0d6c7d86..833e33d6 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,11 @@
+
+Changes to version ?
+------------------------
+
+New features and improvements:
+
+- .NET API: added additional callbacks to control external access to the data model (required to implement RBAC)
+
Changes to version 1.6.1
------------------------
diff --git a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs
index 1980b23b..825f7709 100644
--- a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs
+++ b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs
@@ -22,10 +22,13 @@
*/
using System;
using System.Collections.Generic;
+using System.Data;
using System.Diagnostics;
using System.Runtime.InteropServices;
+using System.Xml.Linq;
using IEC61850.Common;
using IEC61850.TLS;
+using static System.Net.Mime.MediaTypeNames;
using static IEC61850.Client.IedConnection;
// IEC 61850 API for the libiec61850 .NET wrapper library
@@ -2366,15 +2369,75 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
public static extern void IedServer_setListObjectsAccessHandler(IntPtr self, IedServer_ListObjectsAccessHandler handler, IntPtr parameter);
+ ///
+ /// Set a handler to control read and write access to control blocks and logs
+ ///
+ /// IedServer
+ /// handler the callback handler to be used
+ /// a user provided parameter that is passed to the handler
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
public static extern void IedServer_setControlBlockAccessHandler(IntPtr self, IedServer_ControlBlockAccessHandler handler, IntPtr parameter);
+ ///
+ /// Install the global read access handler
+ /// The read access handler will be called for every read access before the server grants access to the client
+ ///
+ /// IedServer
+ /// the callback function that is invoked if a client tries to read a data object
+ /// a user provided parameter that is passed to the callback function
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void IedServer_setReadAccessHandler(IntPtr self, ReadAccessHandler handler, IntPtr parameter);
+
+ ///
+ /// Set a handler to control access to a dataset (create, delete, read, write, list directory)
+ ///
+ /// IedServer
+ /// the callback handler to be used
+ /// a user provided parameter that is passed to the handler
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
public static extern void IedServer_setDataSetAccessHandler(IntPtr self, IedServer_DataSetAccessHandler handler, IntPtr parameter);
+
+ ///
+ /// callback handler to control client read access to data attributes
+ /// User provided callback function to control MMS client read access to IEC 61850
+ /// data objects.The application is to allow read access to data objects for specific clients only.
+ /// It can be used to implement a role based access control (RBAC).
+ ///
+ /// the logical device the client wants to access
+ /// the logical node the client wants to access
+ /// the data object the client wants to access
+ /// the functional constraint of the access
+ /// the client connection that causes the access
+ /// the user provided parameter
+ /// DATA_ACCESS_ERROR_SUCCESS if access is accepted, DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED if access is denied
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ public delegate MmsDataAccessError ReadAccessHandler(IntPtr ld, IntPtr ln, IntPtr dataObject, int fc, IntPtr connection, IntPtr parameter);
+ ///
+ /// Callback that is called when the client is calling a dataset operation (create, delete, read, write, list directory)
+ /// his callback is called before the IedServer_RCBEventHandler and only in case of operations (RCB_EVENT_GET_PARAMETER, RCB_EVENT_SET_PARAMETER, RCB_EVENT_ENABLE
+ ///
+ /// user provided parameter
+ /// client connection that is involved
+ /// one of the following operation types: DATASET_CREATE, DATASET_DELETE, DATASET_READ, DATASET_WRITE, DATASET_GET_DIRECTORY
+ ///
+ /// true to allow operation, false to deny operation
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate bool IedServer_DataSetAccessHandler(IntPtr parameter, IntPtr connection, int operation, string datasetRef);
+ ///
+ /// Callback that is called when a client is invoking a read or write service to a control block or log
+ /// This callback can be used to control the read and write access to control blocks and logs (SGCB, LCBs, URCBs, BRCBs, GoCBs, SVCBs, logs)
+ ///
+ /// user provided parameter
+ /// client connection that is involved
+ /// the ACSI class of the object
+ /// the logical device of the object
+ /// the logical node of the object
+ /// the name of the object (e.g. data object name, data set name, log name, RCB name, ...)
+ /// the name of a sub element of an object or NULL
+ /// access type (read=IEC61850_CB_ACCESS_TYPE_READ or write=IEC61850_CB_ACCESS_TYPE_WRITE)
+ /// true to include the object in the service response, otherwise false
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate bool IedServer_ControlBlockAccessHandler(IntPtr parameter, IntPtr connection, int acsiClass, IntPtr ld, IntPtr ln, string objectName, string subObjectName, int accessType);
@@ -2693,6 +2756,47 @@ namespace IEC61850
return false;
}
+ public delegate MmsDataAccessError InternalReadAccessHandler(LogicalDevice ld, LogicalNode ln, DataObject dataObject, FunctionalConstraint fc, ClientConnection connection, object parameter);
+
+ private InternalReadAccessHandler internalReadAccessHandler = null;
+
+ private object internalReadAccessHandlerParameter = null;
+
+ private ReadAccessHandler readAccessHandler = null;
+
+ public void SetReadAccessHandler(InternalReadAccessHandler handler, object parameter)
+ {
+ internalReadAccessHandler = handler;
+ internalReadAccessHandlerParameter = parameter;
+
+ if (readAccessHandler == null)
+ {
+ readAccessHandler = new ReadAccessHandler (InternalReadHandlerImplementation);
+
+ IedServer_setReadAccessHandler(self, readAccessHandler, IntPtr.Zero);
+ }
+ }
+
+ private MmsDataAccessError InternalReadHandlerImplementation(IntPtr ld, IntPtr ln, IntPtr dataObject, int fc, IntPtr connection, IntPtr parameter)
+ {
+ if (internalReadAccessHandler != null && ld != IntPtr.Zero && ln != IntPtr.Zero)
+ {
+ ClientConnection con = null;
+
+ this.clientConnections.TryGetValue(connection, out con);
+
+ ModelNode ldModelNode = iedModel.GetModelNodeFromNodeRef(ld);
+ ModelNode lnModelNode = iedModel.GetModelNodeFromNodeRef(ln);
+ ModelNode doModelNode = iedModel.GetModelNodeFromNodeRef(dataObject);
+
+ return internalReadAccessHandler(ldModelNode as LogicalDevice, lnModelNode as LogicalNode, doModelNode as DataObject, (FunctionalConstraint)fc, con, internalReadAccessHandlerParameter);
+ }
+
+ return MmsDataAccessError.UNKNOWN;
+ }
+
+
+
private Dictionary writeAccessHandlers = new Dictionary ();
private void ConnectionIndicationHandlerImpl (IntPtr iedServer, IntPtr clientConnection, bool connected, IntPtr parameter)
diff --git a/dotnet/server_example_access_control/Program.cs b/dotnet/server_example_access_control/Program.cs
index c6f069d8..c56a4a75 100644
--- a/dotnet/server_example_access_control/Program.cs
+++ b/dotnet/server_example_access_control/Program.cs
@@ -160,6 +160,38 @@ namespace server_access_control
iedServer.SetDataSetAccessHandler(dataSetAccessHandler, iedServer);
+ /* Install handler to perform read access control on data model elements
+ * NOTE: when read access to a data model element is blocked this will also prevent the client
+ * to read the data model element in a data set or enable a RCB instance that uses a dataset
+ * containing the restricted data model element.
+ */
+ MmsDataAccessError readAccessHandler(LogicalDevice ld, LogicalNode ln, DataObject dataObject, FunctionalConstraint fc, ClientConnection connection, object parameter)
+ {
+ if( dataObject!= null)
+ Console.WriteLine("Read access to "+ld.GetName() + "/"+ln.GetName() + "."+dataObject.GetName() + "\n");
+ else
+ Console.WriteLine("Read access to "+ld.GetName() + "/"+ln.GetName() + "\n");
+
+ if (dataObject == null)
+ {
+ if (ln.GetName() == "GGIO1")
+ {
+ return MmsDataAccessError.OBJECT_ACCESS_DENIED;
+ }
+ }
+ else
+ {
+ if (ln.GetName() == "GGIO1" && dataObject.GetName() == "AnIn1")
+ {
+ return MmsDataAccessError.OBJECT_ACCESS_DENIED;
+ }
+ }
+
+ return MmsDataAccessError.SUCCESS;
+ }
+
+ iedServer.SetReadAccessHandler(readAccessHandler, null);
+
iedServer.Start(102);