High Performance Configurable XDS

High Performance Configurable XDS





Creating an XDS Policy with performance in mind

Extensible Data Security (XDS) in Dynamics 365 Finance and Operations is a powerful framework that enables precise, role-based data access control, ensuring users only see the data they’re authorized to. We also have 2 great community members, Alex Meyer and André Arnaud de Calavon, that post regularly on security and related topics like XDS. Alex wrote this blog article that I would like to remix and re-implement a related scenario that I see somewhat regularly. I’ll be modeling this article a lot after Alex’s article. So, let’s get start.

The Scenario

The scenario we want to solve for is how to filter results on any data source on any form or data entity so a user can only see approved sites as it relates to their worker. This is a different construct from the Warehouse Worker “Approved Warehouses” but uses a similar concept. I want to be able to control through configuration what records a user can see data for based on if that record has a site and if the user is not allowed to see the data for a given site associated to a record.

The Solution

We can use a “bridging table” in our XDS policy plus a method call to get the XDS policy to be user dependent. Knowing this, we can add a tab on the main worker details form to add/remove sites then use that in our policy to create a “data driven” XDS policy. Its data driven because we can alter data in a table to get different results from the policy without having change the policy or any security role assignments to get different results. We can also use 1 security role with a policy applied to it for multiple use cases of sites from no sites to all sites – and everywhere in between for all legal entities. 

Building The Solution

First, let’s create an EDT for the InventSite.RecId called AAInventSiteRecId that extends RefRecId. The reference table for this should be InventSite for field RecId.

Next, let’s create the “bridging table” called AAXInventSiteUser. Add fields from InventSite.InventSiteId, SysUnserInfo.UserId, the EDT we just created, and UserIdRecId plus the supporting relationships. Also the table will need to have the properties Cache Lookup set to EntireTable and the label value should be set. Add a unique index on the InventSiteId and UserId fields and also add non-unique indexes on UserIdRecId and another on InventSiteIdRecId. Add method insert() to lookup and set the RecId values. We will be using the RecId values for lookups but the user-friendly values just for the UI elements we’ll build later. Optionally, you can go back and add code to assign the security role with the security policy on it to a user when they have at least 1 site assigned and remove the role when they have 0 sites assigned.

Next, we need to extend the tables we want to constrain to include the RecId for the associate InventSiteId on the record. This is important because from a performance perspective, the XDS policy will join based on RecId rather than strings so this will greatly improve performance when there are a lot of records to sort through. The process will be approximately the same for each table and it will be as follows:

  1. Extend the table and add the EDT created earlier
  2. Add an index on the field we just added using the EDT
  3. Add a code extension on the table for insert() and update() to set the field we just added based on the InventSiteId selected keeping in mind that 0 may be valid.

As an example, SalesTable is a little unique in that it has a InventSiteId field rather than an InventDimId field but that process remains the same.

  1. Extend SalesTable. Add new field AAXInventSiteRecId using EDT created earlier
  2. Add non-unique index to SalesTable called AAXInventSiteRecId. Add field AAXInventSiteRecId to index. This is important so that SQL can compared on an int64 with an index to another int64 also with an index. There are few other operations in SQL that will be faster.
  3. Add a code extension class for SalesTable. Extend insert() to set the value for AAXInventSiteRecId based on selected InventSiteId if present. Also extend update() to set the value for AAInventSiteRecId.

You can so the same for any table that has InventSiteId on it such as PurchTable, InventDim, and InventSite plus InventLocation too.

Next, let’s add the UI components to add administrator configuration capabilities. First extend the form HCMWorkerV2, adding data sources DirPersonUser and AAXInventSiteUser that was created earlier then add in the UI components where you would like. In the code sample for this, linked below, we also have to add a class to cover some form method extensions to get the desired UI result.

Up to this point, we’ve been doing normal development that has nothing to do with XDS or security specifically but this is where the magic starts.

First, create a query. Add data source AAXInventSiteUser then under that add data source SysUserInfo with a relation between the two on SysUserInfo.RecId. Similar to the article linked above, we’re going to create a range for value “(currentUserId())”.

Next, create a Security Policy. Set the context string to “AAXInventSiteUserPolicy”, the context type to “RoleProperty”, and Operation to allOperations. This can be configured for several options but we want to disable all access types rather than an update or view. Set the Primary Table to AAXInventSiteUser, that we created earlier and set the query to query we just created. After that, we need to add the tables we want to constrain. Constrained tables are the things we are limited data access on. If a table isn’t in the policy, it won’t have XDS applied to it. Here is where we’ll add tables SalesTable, PurchTable, InventDim, InventSite, InventLocation, etc. Add a constrained table with name SalesTable, set constrained to yes and set the to “(SalesTable.AAXInventSiteRecId == AAXInventSiteUser.InventSiteIdRecId)”. This is a free form field with no verification so double check your work. We can then do that again for PurchTable with value (PurchTable.AAXInventSiteRecId == AAXInventSiteUser.InventSiteIdRecId) and also InventDim with value (InventDim.AAXInventSiteRecId == AAXInventSiteUser.InventSiteIdRecId). Keeping constrained tables to a minimum is the general advice that should be followed when creating XDS. 

Finally, we’re going to create a security role to apply the policy. This will be a security role in the AOT. Create the role as you normally would, set the context string to the name of the security policy you just created. Once compiled and database sync’d, when you assign the role, this will activate XDS for the given user.

Testing

In order to test this, we’ll need 2 users. One without the XDS policy who can see the data we want to inspect, and another with the role that applies the XDS Policy. First, our users; Admin and Hope.

Here is out admin user. Nothing special.

However, this is our test user. As a call out, any user with “System Administrator” will bypass XDS.

For Sales Orders, what does admin see? Everything.

But what does our test user see with XDS applied and configured? Just the sites they are set up to see. In this case 1, 2, and 3 in Contoso USMF.

Does that match the sites that our test user is configured to see? Yes.

One call out is if your table isn’t in the list of constrained table, it will be ignored, even if its the primary table make the magic happen. For instance, this is what our test user will see for the site form if we don’t make the InventSite table a constrained table.

All source code is available here if you’d like to install and play with it.

Original Post https://www.atomicax.com/blog-entry/high-performance-configurable-xds

0 Votes: 0 Upvotes, 0 Downvotes (0 Points)

Leave a reply

Join Us
  • X Network2.1K
  • LinkedIn3.8k
  • Bluesky0.5K
Support The Site
Events
August 2025
MTWTFSS
     1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
« Jul   Sep »
Follow
Search
Popular Now
Loading

Signing-in 3 seconds...

Signing-up 3 seconds...