One question I’ve had throughout the releases of D365FO has been why there wasn’t an ‘Undo’ or ‘Remove’ for a security change made within the user interface. Before security changes go live into the system, they first go to a staging area called ‘Unpublished Objects’, on this screen there is currently a ‘Publish all’ and ‘Publish selection’ but there is no way to undo a change once it has been made.
It is a feature that many users have asked about me about so I decided to look to see how feasible it was.
The first thing to do was to figure out which form and SQL tables are being used for this functionality. After some digging I found the form was the SysSecConfiguration and the tables used depended on if you were modifying/removing a security layer from the AOT or if you were creating new security or modifying/removing security originally created in the user interface.
If you were modifying/removing AOT security layers then the tables used were:
- SecurityRoleCustomizeDiskObject
- SecurityDutyCustomizeDiskObject
- SecurityPrivilegeCustomizeDiskObject
If you were creating new security in the user interface or modifying/removing security layers that were originally created in the user interface then the following tables were used:
- SecurityRoleObject
- SecurityDutyObject
- SecurityPrivilegeObject
I also found that an audit of all security changes were stored in the following tables:
- SecurityObjectEvent – shows a high level overview of changes and when that change took effect
- SecurityObjectHistory – shows the entire historical record of each security change, the actual XML data of each change, and a ValidFrom and ValidTo date range
And finally there was the header entry in the SecurityUnpublishedObjects table which stores a record of all security layers that have been modified but not published.
Once all of these pieces were laid out, we need to look at how these tables react to different changes we make in the user interface. As a test, I created a new role in the user interface and modified the LedgerAccountant role.
We can validate that they both show up in the Unpublished Objects area.
If we go to the SQL side we can see both show up here as well. In the SecurityUnpublishedObjects table we can see the header entry for each change I made. You can see that D365FO automatically generates the AOT identifier name for the ‘FP Custom Role’ role as a GUID.
Since I am creating/modifying roles, the next tables to look at would be the SecurityRoleCustomizeDiskObject and SecurityRoleObject tables. One thing to note here is that the LedgerAccountant entry in the SecurityRoleCustomizeDiskObject has an OriginalDiskData column and a Data column. The OriginalDiskData column has the the actual XML security data from the AOT and the Data column in both tables has the customized security changes.
The information in the Data column is the XML that would be generated if you created this object in the AOT, which is how the D365FO Security Converter that I created actually works.
Below is the XML data for the custom role that I created called ‘FP Custom Role’.
In the SecurityObjectEvent table, we can see the two changes made.
The EventType column is an enumeration of the SecurityObjectEventType enum.
In the SecurityObjectHistory table, we get a more detailed view of the change and get to see what the security layer looked like at a particular date time. The ValidFrom and ValidTo dates act like they do for Addresses in the system, basically telling you that a particular security layer’s configuration was valid during this datetime range. So when a change to a security layer occurs the security layer entry that is currently active has its ValidTo field set to the current datetime, a new entry is created for the security layer in this table and the ValidFrom date is set to the current datetime, the ValidTo date is set to datetime MAX, and the changed security is set in the Data column.
In the example below, the LedgerAccountant entry in row 4 was the initial security and it was modified with the entry in row 1.
If we wanted to remove or undo a change to a security layer that exists in the AOT, this is a fairly straight forward process:
- Remove the entry from the corresponding Security<Layer>CustomizeDiskObject table (so in our example where we modified the LedgerAccountant role we would remove the records from the SecurityRoleCustomizeDiskObject)
- Remove the corresponding entries from the SecurityObjectEvent and SecurityObjectHistory tables so our audit trail of these security layers stays accurate
- Remove the entry from the SecurityUnpublishedObjects table so it wouldn’t show up as an entry in System Administration -> Security Configuration -> Unpublished Objects tab
The situation becomes more complex when we want to undo or remove changes we made to security layers that were originally created in the user interface. As we cannot simply remove the records from the Security<Layer>Object tables as this would remove the entire security layer itself. Instead we would have to do the following steps:
- Copy the XML data from the SecurityObjectHistory table
- Take this XML data and update the Data field of the corresponding security layer in the correct Security<Layer>Object table
- Remove the entries created by the customizations done in the SecurityObjectEvent and SecurityObjectHistory tables
- Update the ValidTo field to DateTime MAX on the SecurityObjectHistory table of the current security layer
- Remove the entry from the SecurityUnpublishedObjects table so it wouldn’t show up as an entry in System Administration -> Security Configuration -> Unpublished Objects tab
Conclusion
As you may have gathered from from the screenshots above, I am working my way through creating this functionality and having it be a part of our Security Designer offering at Fastpath as a part of a larger piece of functionality surrounding reporting on security changes. If you are going to try and implement this yourself, there are some things to keep in mind:
- None of these tables are exposed to the AOT so all interactions with them will have to be done via a direct SQL connection
- Modifying actual audit data is not something I normally recommend (especially working for a security, audit, and compliance company), but we do want our audit trail of each security object to be correct
- Need to be sure that you can handle all security situations as an error during this undo/remove process can leave your system in an unsafe state from a security perspective
I hope this walk through helps answer this question that many have had, if you have any questions feel free to reach out!
I have used the same privilege in two different roles but permissions granted for same privilege are different in two different roles. Permissions are changed from UI only. In this scenario, permissions set later is overriding the earlier one. I am unable to delete the role from unpublished tab. How to encounter this scenario?
Ripan,
I am unclear on what your issue is, a privilege assigned to two different roles will grant the same access to both roles but other objects in each individual role may grant different access.
Changes to security done in the user interface are initially stored in the Unpublished Objects area and then must be published for them to become ‘live’ security.
If you want to remove an unpublished object, you will need access to the database (can only be done in non-prod environments) and must be done manually as there is no way to do this via the UI.
Were you able to find a way to “undo” a pending security change via x++ or the UI?
Don,
There’s not a way to natively undo a security change via the UI, you can do it via manual SQL statements on the database (following the steps listed in the blog post).
I have not tried it via X++ but I the tables you would need to interact with are system tables and are not listed in the AOT so you would have to use methods from the SysSecurity class.