Field Level Security using Events in Dynamics NAV 2016
One of the cool new features in Dynamics NAV 2016 is the options to use Events.
Through events you can create functions that are published and subscribe to them in other objects. The great part here is that Microsoft have incorporated a long list of published functions as part of the standard application which can be subscribed to in custom code. This will simplify maintaining and upgrading the objects since modifications can be done completely separated from the standard objects.
To demonstrate this new feature I have programmed a simple field level security add-on (maybe add-on is the wrong word, but at least something that can be added to the standard Dynamics NAV 2016 application to control who are allowed to modify certain fields).
Field level security is something that is missing in standard NAV and almost always something that is discussed during implementations, so I thought this was a good example of how to use Events in NAV 2016 (kind of killing two birds with one stone).
The overall concept for this is to have two new tables where we can setup what fields that are restricted to only be modified by certain users and then to use the triggers that exists in codeunit 1 to check the data before it is committed to the database (by simply comparing restricted fields on the old record with the new record).
It is important to know that this solution will not prevent users from seeing the data in the fields but it can be used to define who is allowed to change the value of the fields. If you want to prevent users from seeing the fields then the property called AccessByPermission on the fields could be used (but that’s another topic and maybe a future blog post).
In my example I have two new tables; one called Restricted Field and the other called Restricted Field User Access (for lack of a better name). The objects can be downloaded in the download section and used as reference.
The Restricted Field table is used to define what fields that should be restricted from the general user, it has a table no. and field no. field (and their names as non-editable flowfields to make it easier for the user).
The next table, Restricted Field User Access (available from the User Access button in the ribbon of the Restricted Field page), is then where you define what users that are allowed to change each of the restricted fields.
In the above example I have defined that the Blocked field in the Item table is a restricted field and only the user OSIMREN can modify it. The Restricted Field User Access table obviously have the table no. and field no. as part of the primary key (as well as the user id).
Now we have the structure to use, it is time to write the code (the fun stuff :)).
Those new functions are created with the property Event = Publisher and EventType = Integration. This means that the functions are published and can be subscribed to by other functions in the application.
The EventType property have two values; business and integration. The business event type should be used if the function is formally defined and will not change in the future, the integration event should be used when the function potentially can change. At least that is my interpretation of Microsoft’s description of the difference. I will be using the integration option.
It is worth knowing that there is a third type of event; trigger events. Those are predefined events that gets published by the runtime for the table and page triggers. This means that you can also subscribe to the OnInsert, OnModify, OnDelete, etc.. of all the tables (and also for field validation code). So, theoretically there should be no need to write any code in the table triggers of the standard objects anymore. Quite a big change to how you can write code I would say, but a super nice addition.
We now create a new codeunit with functions that are subscribing to the published functions in codeunit 1. The functions in our new codeunit are then called each time the published function is called.
I my example I created a Restricted Fields Mgt. codeunit with the following functions; OnAfterGetDatabaseTableTriggerSetup, OnAfterOnDatabaseDelete, OnAfterOnDatabaseInsert, OnAfterOnDatabaseModify, OnAfterOnGlobalRename that are all subscribing to the corresponding functions in codeunit 1. The subscription is done by setting the Event property to Subscriber and in the EventPublisherObject property select the object (codeunit 1) and in the EventFunction select the published function to subscribe to.
We also make the codeunit a single instant codeunit and put the two new tables in it as global variables. This will reduce the number of reads that NAV will have to do from the database since the two tables will then remain in memory during the client session. This is the same way the change log codeunit is structured.
The code in the OnAfterGetDatabaseTableTriggerSetup function just returns true if the record that is being changes is in a table that have a restricted field. Returning true will make NAV call the OnAfterOnDatabase functions, otherwise those are not called (same concept as the change log again).
Then in the other functions we just compare the value of the restricted fields on the record being changed with the record that is in the database. We do this using RecordRef and FieldRef variables, the key here is that the record we get from the database is the ‘unmodified’ record that we can compare with, and we are only comparing the restricted fields.
That’s it! An application wide feature made without changing a single standard objects.
Let’s test it; I go to an item and if I try to change the inventory posting group I will get the following error message when the record is about to be committed to the database (it is important to know that it is when the record is a about to be committed and not when the fields is validated, so basically when you close the page).
The error message might have a need to be made more user friendly (like including the field name and the value before and after), but I think the above servers the purpose of demonstrating the concept of the Events.
You can download the objects from the downloads section, just keep in mind that the code is for illustration only, you can use it as you want but I leave no warranties.
It is also good to know that you can view all the event subscriptions from the development environment by going to Tools -> Debugger -> Event Subscriptions…
Events are my favorite new feature! Happy Eventing!