How to bypass table security in X++ coding
For various reasons, you would like to read business data from or write data to the Dynamics 365 F&O database without enforcing security in the user context. For example, a user might not have access to various tables, like parameters or configuration. In this post, I talk about how to access and write data using the X++ unchecked
statement.
Table permissions
Before diving into the coding part, first some words about table security. Dynamics 365 F&O is using a pessimistic model for security permissions. If you don’t have a role assigned, you will not have menu items to open specific features. Only menu items are visible in case the role grant the permissions. As a business user, you get access to data by opening pages and forms where tables are used as data sources. Depending on the contents of a role, a user can also access data by using the Excel add-in or export data via Data management.
By opening forms, the users will get indirect access to table contents as the data sources will inherit the permissions from the privilege for the menu item. In case a user has read permissions for the customer groups form, the access to the table is with read permissions also. The user can’t create, update or delete records.
So from the point of a business user, the data is secured as they only get portions of the application and can interact with the data intended for them. This statement is true in case the security roles are set up and assigned to users in the correct way.
Table access in coding
Now we come to an interesting part of the Dynamics 365 F&O security framework. The title of the post is about bypassing table security in coding. What should a developer do to be able to retrieve e.g. customer parameters where the user does not have access himself? The answer might be unexpected, but in 99% of the cases, the developer can forget about the table permissions. When coding is used to retrieve the record of the customer parameters, or access customer details or accounting entries, the data is retrieved from the database without checking the permissions.
When mentioning 99%, this is not 100%. In what conditions should there be some awareness or action from the developer? There are two areas in the security framework where a check on table access is required or not all records are returned.
One is called the Table Permisisons Framework (TPF). I explained this in an earlier post on this site: Securing a menu item is not enough – table permissions. There are over 20000 tables in the application where not even 200 are secured with the table permissions framework. In case an AOS authorization is required for table and/or fields, it can be solved by providing table permissions on privileges or the security role.
The second feature is eXtensubke Data Security (XDS). In case security policies are created, depending on the context, not all records will be returned from the database. I wrote several posts about XDS before: XDS Archives – Dynamicspedia
Why do I talk about bypassing the security in coding? There are workarounds possible, like providing table permissions or use a view for accessing a specific table which is not recognized by the XDS framework in case the view is not within the list of constrained tables. The answer is that for some tables you don’t want to provide table permissions as this might open options for data loss or unwanted access. The tables part of TPF do have sensitive data or should be protected to avoid security issues or data privacy compliancy violations (e.g. GDPR).
When developing a solution, you don’t know if there will be an additional security policy implemented or what exactly will be configured at a client for custom security roles. In the case you want to ensure your coding will work and processes all detail correctly, you can think of adding a statement to bypass table permissions.
Unchecked statement
Finally, we are at the point to talk about bypassing TPF or XDS in X++ coding. The unchecked
statement is introduced in Dynamics 365 and has options to ensure you will be able to read, create, update, or delete data in case it is needed in coding.
If you want to access e.g. the worker bank account table, you would usually use a statement like below.
HcmWorkerBankAccount workerBankAccount;
select firstonly workerBankAccount
where workerBankAccount.Worker == _workerRecId;
As the HcmWorkerBankAccount
table has an additional authorization requirement for all CRUD actions, this can give a runtime error to the user if there are no explicit table permissions set via the security. To ensure your coding will run correctly, you can use the same coding wrapped in an unchecked
block. See here an example.
HcmWorkerBankAccount workerBankAccount;
unchecked(Uncheck::TableSecurityPermission)
{
select firstonly workerBankAccount
where workerBankAccount.Worker == _workerRecId;
}
In case a table can be restricted with a security policy, a loop will not return all records. To ensure your coding will return all records, you can also use an unchecked
block in the next way.
unchecked(Uncheck::XDS)
{
//Your coding comes here
}
A great example where Microsoft use this themselves is in the XDS()
method on the table MyLegalEntities
. This is to ensure the correct values will be inserted in the temporary table regardless having a security policy active.
When you use the unchecked
statement, you must set a parameter value for the option Uncheck
. This is an enumeration with three options. Two options are used above in the examples and will bypass either the Table Permissions Framework or eXtensible Data Security. The option None
is a default value and will not bypass any security. Technically it is possible to build the coding where you didn’t provide a value for the Uncheck
parameter, but this will then not have any effect; it will not bypass the security.
There is more…
Eventhough the chance is really low that you would need the unchecked
statement, it will be wise to overthink this when defining a new solution. In case you implement this statement, ensure that your coding will be executed securely, e.g. by an action menu item as this can be managed with security.
I used this statement in the Entra group ID enhancement recent added to the D365FO Admin toolkit: Enhanced Entra ID group integration in D365FO Admin Toolkit. The use case here was that the sync should run correctly when a business user starts a new Dynamics 365 session. We don’t expect to grant access permissions for the option to insert or delete records from security related tables to a normal business user. As Entra ID group settings already defined what roles should be assigned to the user, it was a safe decision using the unchecked
statement.
I do hope you liked this post and will add value for you in your daily work as a professional. If you have related questions or feedback, don’t hesitate to use the Comment feature below.
That’s all for now. Till next time!
Leave a Reply
Want to join the discussion?Feel free to contribute!